Шаблон аргументов non-type для ссылочного типа и odr-used

Является ли переменная v в примере кода ниже odr-used?

extern void* v;

template<void*&>
void f() {}

int main()
{
    f<v>();
}

Я нашел этот шаблон в Boost ML.

ср. http://lists.boost.org/Archives/boost/2011/04/180082.php

В нем говорится, что boost::enabler никогда не определяется, но clang отклоняет его как ошибку привязки, если предоставляется опция -g.

ср. http://melpon.org/wandbox/permlink/nF45k7un3rFb175z

Пример кода выше - это уменьшенная версия кода Boost ML, а clang тоже отклоняет его.

ср. http://melpon.org/wandbox/permlink/ZwxaygXgUhbi1Cbr

Я думаю (но я не уверен), что аргументы шаблона non-type для ссылочного типа используются odr, даже если они не указаны в их шаблоне, поэтому шаблон Boost ML плохо сформирован.

Правильно ли я понимаю?

Ответы

Ответ 1

[basic.def.odr]/3:

Переменная x, имя которой отображается как потенциально оцененное выражение ex является odr-используемым ex , если не применить преобразование lvalue-to-rvalue (4.1) до x дает постоянное выражение (5.19) [..]

К сожалению, применение преобразования l-t-r в v в этот момент не даст постоянного выражения - [expr.const]/2:

Условное выражение e является основным постоянным выражением, если только оценка e, следуя правилам абстрактной машины (1.9), оценил бы одно из следующих выражений: [..]

- преобразование lvalue-to-rvalue (4.1), если оно не применяется к

  • нелетучий glvalue интегрального или перечисляемого типа , который ссылается на нелетучий объект const с предшествующим инициализация, инициализированная константным выражением [..] или

  • нестабильный glvalue , который ссылается на энергонезависимый объект, определенный с помощью constexpr, или который ссылается        к не изменяемому под-объекту такого объекта, или

  • нелетучий glvalue литерального типа , который относится к энергонезависимому объекту, срок службы которого начинался с оценки e;

Однако, хотя реализация, предложенная Мэттом, неверна, идея, безусловно, такова. Простой способ использования этого подхода демонстрируется в этом ответе, используя шаблон-помощник. В вашем случае попробуйте

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{}; }
};

Демо.

Ответ 2

Я считаю, что v используется odr. f<v> - это идентификатор шаблона (14.2), шаблонный аргумент которого является id-выражением ( 5.1.1) - формой выражения. Это явно не неоперабельный операнд (он не отображается как операнд typeid, sizeof, noexcept или decltype), поэтому он потенциально оценивается за 3.2/2:

3.2/2 Выражение потенциально оценивается, если оно не является неоцененным операндом (раздел 5) или его подвыражением...

В этот момент имеем

3.2/3 Переменная x, имя которой отображается как потенциально оцениваемое выражение ex, используется odr, если только [условие, которое, как представляется, не применяется здесь как no lvalue- к-rvalue применяется].