Нетиповые (справочные) параметры шаблона и связь

В следующих,

int i{3};
const int j{3};
extern const int k{3};

template <typename T, T&>
void f() {}

int main()
{
    f<int, i>();        // OK
    f<int const, j>();  // not valid template argument: 'j' has not external linkage
    f<int const, k>();  // OK
}

GCC выдает ошибку при использовании j в качестве параметра шаблона, в то время как clang компилируется нормально.

  • Какая связь между i, j?
  • почему есть разница между const / не const?
  • кто прав? GCC или лязг?

person iavr    schedule 05.05.2014    source источник
comment
Переменные const с ограниченным пространством имен имеют внутреннюю связь.   -  person Kerrek SB    schedule 05.05.2014
comment
Важно отметить, что разница между компиляторами заключается не в том, считают ли они, что переменная имеет внутреннюю / внешнюю связь, а в том, принимают ли они это как допустимый аргумент шаблона, не являющийся типом.   -  person David Rodríguez - dribeas    schedule 05.05.2014
comment
@ Дэвид Я понял, спасибо!   -  person iavr    schedule 05.05.2014


Ответы (3)


Как указывает Керрек в комментарии, переменные уровня пространства имен const имеют внутреннюю связь (если вы не используете ключевое слово extern). В C ++ 03 нельзя использовать указатель или ссылку на переменную с внутренней связью в качестве аргумента шаблона, не являющегося типом. Это ограничение снято в C ++ 11. Кажется, что ваша версия gcc играет по правилам C ++ 03, а компилятор clang использует правила C ++ 11.


14.3.2 [temp.arg.nontype] / 1

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

  • [...]
  • постоянное выражение (5.19), которое обозначает адрес объекта с длительностью статического хранения и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая не- статические члены класса, выраженные (без учета скобок) как & id-expression, за исключением того, что & может быть опущено, если имя относится к функции или массиву, и должно быть опущено, если соответствующий параметр шаблона является ссылкой; или
  • [...]
person David Rodríguez - dribeas    schedule 05.05.2014
comment
Спасибо. Я пробовал с 4.8.1, 4.8.2 и 4.9.0 (март 2014 г.). Такое же поведение. Возможная ошибка? - person iavr; 06.05.2014
comment
@iavr: Вы строите с -std=c++11 или без него? Если вы собираете C ++ 11 и это не удается, то это ошибка. Clang ++ в основном вырос с мышлением C ++ 11, он (неправильно?) Делает некоторые вещи так же, как C ++ 11, даже в режиме C ++ 03. - person David Rodríguez - dribeas; 06.05.2014
comment
С -std=c++11 или c++1y. Проверьте 4.8.2 онлайн здесь. - person iavr; 06.05.2014
comment
@iavr: Значит, это ошибка компилятора. В ответ добавлена ​​конкретная цитата из стандарта. Цитата ясна внешняя или внутренняя ссылка - person David Rodríguez - dribeas; 06.05.2014

i имеет внешнюю связь, а j - внутреннюю. Эти правила перечислены в разделе §3.5 [basic.link]

4 Безымянное пространство имен или пространство имен, объявленное прямо или косвенно в безымянном пространстве имен, имеет внутреннюю связь. Все остальные пространства имен имеют внешнюю связь. Имя, имеющее область действия пространства имен, которому не была присвоена внутренняя связь выше, имеет ту же связь, что и охватывающее пространство имен, если это имя
- переменной; или
- ...

Глобальное пространство имен имеет внешнюю связь, следовательно, i также имеет внешнюю связь (поскольку она явно не объявлена ​​как имеющая внутреннюю связь).

3 Имя, имеющее область пространства имен (3.3.6), имеет внутреннюю связь, если это имя
- ...
- энергонезависимой переменной, которая является явно объявленные const или constexpr, но не объявленные явно extern и ранее не объявленные как имеющие внешнюю связь; или
- ...

j явно объявлен const без объявления extern, следовательно, он имеет внутреннюю связь.

Я считаю, что clang является правильным в этом случае из-за §14.3.2 / 1 [temp.arg.nontype]

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

j удовлетворяет вышеуказанному требованию и должен быть разрешен как аргумент, не являющийся типом.

person Praetorian    schedule 05.05.2014
comment
Разве &j не обозначает адрес, а j обозначает переменную? Или это ссылочная переменная означает, что она обозначает адрес? - person Yakk - Adam Nevraumont; 05.05.2014
comment
@Yakk Я предположил последнее. Например, формирование ссылки на член класса static const, у которого отсутствует определение, приводит к ошибке компоновщика, как и получение его адреса. - person Praetorian; 05.05.2014
comment
@Praetorian: Согласитесь, немного помахал рукой, но пример не так хорош. Определение требуется для члена класса static const (или интегрального типа), потому что он odr-used, а привязка ссылки или получение адреса являются odr-uses. Но да, кроме педантичных подробностей, чтобы сослаться на объект, нужно знать, где он живет, его адрес. - person David Rodríguez - dribeas; 05.05.2014

Это ошибка, однако она известна (и она еще не реализована, по крайней мере, до gcc 4.9).

Вот отчет об ошибке.

Я ожидаю, что в gcc 5.0 это будет реализовано, поскольку 5.0 добавляет много новых функций C ++ 11.

person cpp-newby    schedule 05.07.2015