Шаблон доступа к символу в безымянном пространстве имен

Мы обновляем наш компилятор XL C/C++ с V8.0 до V10.1 и обнаружили некоторый код, который теперь выдает нам ошибку, даже если он скомпилирован под V8.0. Вот минимальный пример:

test.h:

#include <iostream>
#include <string>

template <class T>
void f()
{
  std::cout << TEST << std::endl;
}

test.cpp:

#include <string>
#include "test.h"

namespace
{
  std::string TEST = "test";
}

int main()
{
  f<int>();
  return 0;
}

Под V10.1 мы получаем следующую ошибку:

"test.h", line 7.16: 1540-0274 (S) The name lookup for "TEST" did not find a declaration.
"test.cpp", line 6.15: 1540-1303 (I) "std::string TEST" is not visible.
"test.h", line 5.6: 1540-0700 (I) The previous message was produced while processing "f<int>()".
"test.cpp", line 11.3: 1540-0700 (I) The previous message was produced while processing "main()".

Мы нашли похожую разницу между g++ 3.3.2 и 4.3.2. Я также нашел в g++, если я переместить #include "test.h" быть после безымянного объявления пространства имен, ошибка компиляции исчезает.

Итак, вот мой вопрос: что Стандарт говорит об этом? Когда создается экземпляр шаблона, считается ли этот экземпляр объявленным в тот момент, когда был объявлен сам шаблон, или стандарт не очень ясен по этому вопросу? Я хоть немного посмотрел черновик n2461.pdf, но ничего толкового не придумал.

3 ответа

Решение

Это не правильный код C++. TEST не зависит от параметра шаблона T, поэтому он должен быть найден в контексте определения шаблона при его анализе. Однако в этом контексте нет декларации TEST существует, и поэтому есть ошибка.

Диагностическое сообщение для этого неправильно сформированного шаблона может быть отложено до момента его создания компилятором, но если компилятор исправен, он диагностирует ошибку раньше. Компиляторы, которые не выдают диагностическое сообщение для этого кода, даже когда создается экземпляр шаблона, не соответствуют. Это не имеет ничего общего с безымянными пространствами имен.

Кроме того, обратите внимание, что даже если вы поместите безымянное пространство имен над этим шаблоном, оно не будет действительной программой C++, если вы определите и вызовете этот шаблон в нескольких единицах перевода. Это связано с тем, что разные экземпляры одного и того же шаблона с одинаковыми аргументами шаблона будут ссылаться на разные вещи (строка в безымянном пространстве имен будет создавать новый объект каждый раз, когда он определен в другом модуле перевода). Поведение для такой программы будет неопределенным.

Помните, что #include просто копирует содержимое файла.h в файл.cpp. Итак, ваше определение f() появляется перед определением TEST. Лучший способ обойти это - добавить

extern std::string TEST;

в верхней части вашего.h файла.

Этот тест не проходит онлайн-компилятор Comeau, который в прошлом показывал себя как один из самых совместимых со стандартами компиляторов. Тогда я бы склонялся к тому, чтобы код был неправильным в том виде, в котором он написан, хотя я не мог указать вам на строку в стандарте относительно того, почему. Обратите внимание, что компиляция кода в расслабленном режиме завершается успешно.

Другие вопросы по тегам