Передача структуры в шаблон с extern const. Для чего нужен экстерн?

Я спрашиваю себя, почему работает следующий код и что спецификатор extern делает при создании baz_instance:

struct baz {
    int value;
};

extern const baz baz_instance = {3};

template<baz const& b>
int foo(){
    return b.value;
}

int main(){
    foo<baz_instance>();
    return 1;
}

Почему этот код компилируется в первую очередь и почему он больше не компилируется, если спецификатор extern отсутствует? Что делает спецификатор extern в этом примере?

Ответ 1

Это одна из частей стандарта, которая изменилась с С++ 03 на С++ 11.

В С++ 03 [temp.arg.nontype] читает:

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

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

В С++ 11, который обновился в результате issue 1155, хотя GCC по-прежнему имеет ошибка в отношении этого поведения:

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

В С++ 14 это упростилось еще больше и даже не упоминает о связи.

Что касается вашего конкретного вопроса, спецификатор extern добавляет внешнюю привязку к baz_instance. Без него baz_instance имеет внутреннюю связь. В С++ 03 вам понадобилась внешняя связь, чтобы иметь тип шаблона типа non-type ссылочного типа. В С++ 11 вы больше не работаете, поэтому extern больше не требуется, и он компилируется без него.

Ответ 2

Из 14.3.2.1 стандартные состояния:

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

  • адрес объекта или функции с внешней связью, включая шаблонов функций и шаблонов-шаблонов функций, но исключая нестатические члены класса, выраженные как и id-выражение, где и является необязательным, если имя относится к функции или массиву, или если соответствующая шаблон-параметр является ссылкой;

От fooobar.com/questions/370295/... от mweerden

Ответ 3

Ключевое слово extern означает, что он будет иметь внешнюю связь, другими словами, символ будет экспортироваться при компиляции единицы перевода. Поскольку ваш тип const, он по умолчанию имеет внутреннюю привязку (как будто он был объявлен static). Шаблоны не могут зависеть от типов, которые имеют только внутреннюю связь.

Мне бы хотелось узнать причину, почему, но кажется, что она потеряла пески времени: Почему С++ 03 требует, чтобы параметры шаблона имели внешнюю привязку?.

Ответ 4

Ключевое слово extern указывает, что переменная была определена в другом модуле компиляции (исходный файл).

Итак, в вашем случае baz должен быть определен в другом исходном файле, а extern - это способ сказать, что это переменная, не определенная в этом исходном файле, но в другом, и вы найдете его во время компиляции.

Ключевое слово extern означает "объявить без определения". Другими словами, это способ явно объявить переменную или принудительно объявить без определения.