Is the variable v
in the sample code below odr-used?
extern void* v;
template<void*&>
void f() {}
int main()
{
f<v>();
}
I found this pattern in Boost ML.
cf. http://lists.boost.org/Archives/boost/2011/04/180082.php
It says that the boost::enabler
is never defined, but clang rejects it as a linkage error if -g
option is provided.
cf. http://melpon.org/wandbox/permlink/nF45k7un3rFb175z
The sample code above is reduced version of the Boost ML's code and clang rejects it too.
cf. http://melpon.org/wandbox/permlink/ZwxaygXgUhbi1Cbr
I think (but I am not sure) that template non-type arguments for reference type are odr-used even if they are not referred in their template body so the Boost ML's pattern is ill-formed.
Is my understanding correct?
A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type.
Non-type template arguments are normally used to initialize a class or to specify the sizes of class members. For non-type integral arguments, the instance argument matches the corresponding template parameter as long as the instance argument has a value and sign appropriate to the parameter type.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
I believe v
is odr-used. f<v>
is a template-id (14.2) whose template-argument is an id-expression (5.1.1) - a form of expression. It's clearly not an unevaluated operand (it doesn't appear as an operand of typeid
, sizeof
, noexcept
or decltype
), so it's potentially evaluated per 3.2/2:
3.2/2 An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof...
At which point, we have
3.2/3 A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used unless [a condition that doesn't appear to apply here as no lvalue-to-rvalue conversion is applied].
[basic.def.odr]/3:
A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.19) [..]
Unfortunately, applying the l-t-r conversion to v
at this point would not yield a constant expression - [expr.const]/2:
A conditional-expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: [..]— an lvalue-to-rvalue conversion (4.1) unless it is applied to
a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression [..], or
a non-volatile glvalue that refers to a non-volatile object defined with
constexpr
, or that refers to a non-mutable sub-object of such an object, or- a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of
e
;
However, though the implementation suggested by Matt isn't correct, the idea certainly is. A simple way of using this approach is demonstrated in this answer, using a helper template. In your case, try
template <bool cond, int id=0>
using distinct_enable_if =
typename std::enable_if<cond, std::integral_constant<int,id>*>::type;
class test
{
public:
template< class... T,
distinct_enable_if<sizeof...(T) == 10> = nullptr>
test( T&&... ) {}
template< class T,
distinct_enable_if<std::is_arithmetic<T>{}> = nullptr>
operator T() const { return T{}; }
/* Note the additional template argument:
It ensures that the template parameter lists are not identical,
and the ODR isn't violated */
template< class T,
distinct_enable_if<std::is_pointer<T>{}, 1> = nullptr>
operator T() const { return T{}; }
};
Demo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With