14

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?

Paolo M
  • 12,403
  • 6
  • 52
  • 73
Mitsuru Kariya
  • 938
  • 4
  • 14

2 Answers2

7

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 expression ex is odr-used unless [a condition that doesn't appear to apply here as no lvalue-to-rvalue conversion is applied].

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
2

[basic.def.odr]/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x 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 of e, 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.

Community
  • 1
  • 1
Columbo
  • 60,038
  • 8
  • 155
  • 203