Подтвердить что ты не робот

Ссылка как аргумент шаблона не-типа

В приведенном ниже примере попытка использовать переменную ссылочного типа в качестве аргумента для параметра шаблона непигового типа (самого ссылочного типа). Clang, GCC и VС++ все это отвергают. Но почему? Я не могу найти ничего в стандарте, что делает его незаконным.

int obj = 42;
int& ref = obj;

template <int& param> class X {};

int main()
{
    X<obj> x1;  // OK
    X<ref> x2;  // error
}

Живой пример


CLang говорит:

source_file.cpp: 9: 7: ошибка: аргумент шаблона непигового типа ссылочного типа 'int &' не является объектом

Другие жалуются аналогичным образом.


Из стандарта (все кавычки из С++ 11; С++ 14 не имеет существенных изменений в соответствующих частях):

14.3.2/1 Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из:

...

  • постоянное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью... выражается (игнорируя круглые скобки) как & id-expression, за исключением того, что &... должны быть опущены, если соответствующий шаблон-параметр является ссылкой

...

Теперь какое постоянное выражение:

5.19/2 Условное выражение является выражением основной константы, если оно не включает одно из следующих значений в качестве потенциально оцененного подвыражения (3.2)...

...

  • id-выражение, которое ссылается на переменную или элемент данных ссылочного типа, если ссылка не имеет предыдущей инициализации, инициализированной константным выражением

...


Насколько я могу судить, ref in X<ref> - это id-выражение, которое ссылается на переменную ссылочного типа. Эта переменная имеет предыдущую инициализацию, инициализированную выражением obj. Я считаю, что obj является постоянным выражением, и в любом случае, если это не так, тогда X<obj> тоже не должен компилироваться.

  • Так что мне не хватает?
  • Какое предложение в стандарте делает недействительным X<ref>, а X<obj> является допустимым?
4b9b3361

Ответ 1

Введение

Верно сказать, что имя ссылки - это id-выражение; id-выражение не ссылается на ссылку, на которую ссылается ссылка, но сама ссылка.

 int    a = 0;
 int& ref = a; // "ref" is an id-expression, referring to `ref` - not `a`

Стандарт (N4140)

Вы цитируете соответствующие разделы стандарта в своем сообщении, но вы не указали наиболее важную часть (подчеркните мою):

14.3.2p1 Шаблоны без аргументов [temp.arg.nontype]

Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:

  • <суб > ...суб >

  • постоянное выражение (5.19), которое обозначает адрес полного объекта со статической продолжительностью стеллажа и внешней или внутренней связью или функцией с внешней или внутренней связью, включая функциональные шаблоны и функцию template-ids, но исключая нестатические члены класса, выраженные (игнорируя круглые скобки) как & id-expression, , где id-выражение - это имя объекта или функции, за исключением того, что & может быть опущено, если имя относится к функции или массиву и должно быть опущено, если соответствующий шаблон-параметр является ссылкой; <Суб > ...суб >


Примечание: В более ранних черновиках "где id-выражение - это имя объекта или функции" отсутствует; он был рассмотрен DR 1570 - это, несомненно, делает намерение более ясным.


Переменная ссылочного типа не является объектом?

Вы абсолютно правы; сама ссылка имеет ссылочный тип и может просто действовать как объект, когда часть выражения.

5p5 Выражения [expr]

Если выражение первоначально имеет тип "ссылка на T" (8.3.2, 8.5.3), тип доводится до T до любого дальнейшего анализа. Выражение обозначает объект или функцию, обозначенные ссылкой, а выражение представляет собой значение lvalue или значение x, в зависимости от выражения.


Разработка

Очень важно отметить, что константное выражение ( "которое обозначает адрес полного объекта..." ) должно быть одним из &id-expression или id-expression.

Несмотря на то, что константное выражение, а не просто выражение id, может ссылаться на объект со статической продолжительностью хранения, мы не можем использовать его для "инициализации" шаблона-параметра ссылочного или указательного типа.

Пример фрагмента

template<int&>
struct A { };

int            a = 0;
constexpr int& b = (0, a); // ok, constant-expression
A<(0, a)>      c = {};     // ill-formed, `(0, a)` is not an id-expression

Примечание. Это также является причиной того, что мы не можем использовать строковые литералы как шаблонные аргументы; они не являются id-выражениями.