Фактический результат разрешения имен в шаблоне класса отличается от стандарта C++ 03
Я тестирую код в стандарте C++ ISO/IEC 14882-03 14.6.1/9 на Xcode 4.1 и Visual Studio 2008. Выводы обоих компиляторов отличаются от ожидаемого результата стандарта.
Код вставлен ниже.
#include <stdio.h>
#include <iostream>
using namespace std;
void f(char);
template <class T > void g(T t)
{
f(1);
f(T(1));
f(t);
}
void f(int);
void h()
{
g(2);
g('a');
}
void f(int)
{
cout << "f int" << endl;
}
void f(char)
{
cout << "f char" << endl;
}
int main() {
h();
return 0;
}
Как описание стандарта. Ожидаемый результат должен быть
f char
f int
f int
f char
f char
f char
Создайте и запустите код на Xcode 4.1. Вывод как показано ниже. В настройках сборки я попытался изменить "Компилятор для C/C++/Object-C" на Apple LLVM Compiler 2.1, Gcc 4.2 и LLVM GCC 4.2. Выходы одинаковые.
f char
f char
f char
f char
f char
f char
Создайте и запустите код в Microsoft Visual Studio 2008. Вывод будет таким, как показано ниже.
f int
f int
f int
f int
f char
f char
Описание (14.6.1/9) стандарта приведено ниже.
Если имя не зависит от параметра-шаблона (как определено в 14.6.2), объявление (или набор объявлений) для этого имени должно находиться в области действия в той точке, где имя появляется в определении шаблона; имя привязано к объявлению (или объявлениям), найденному в этой точке, и на эту привязку не влияют объявления, видимые в момент создания экземпляра. [Пример:
void f(char);
template<class T> void g(T t)
{
f(1); // f(char)
f(T(1)); // dependent
f(t); // dependent
dd++; // not dependent
}
void f(int);
double dd;
void h()
{
// error: declaration for dd not found
g(2); // will cause one call of f(char) followed // by two calls of f(int)
g(’a’); // will cause three calls of f(char)
- конец примера]
Код корректно сформирован для компиляторов, но результаты отличаются. Было бы очень опасно переносить этот код на разные платформы.
Кто-нибудь знает, почему эти компиляторы не следуют стандарту?
Изменить 10/11/2011
Согласно http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html, пример в стандарте неверен. Я тестирую код ниже на Clang и Gcc.
#include <stdio.h>
#include <iostream>
using namespace std;
void f(char);
template <class T > void g(T t)
{
f(1);
f(T(1));
f(t);
}
enum E{ e };
void f(E );
void h()
{
g(e);
g('a');
}
void f(E )
{
cout << "f E" << endl;
}
void f(char)
{
cout << "f char" << endl;
}
int main() {
h();
return 0;
}
Выход как и ожидалось.
f char
f E
f E
f char
f char
f char
Спасибо,
Джеффри
3 ответа
Как отмечено в первом примере, это экземпляр двухфазного поиска имени, который реализован как GCC, так и Clang, а MSVC - нет. И в этом случае и GCC, и Clang верны: на самом деле это неправильный стандарт, как отмечается в отчете о дефектах ядра C++ № 197. Стандарт C++11 содержит другой пример.
Это одна из наиболее распространенных проблем, с которыми мы сталкиваемся при переносе кода в Clang либо из MSVC (который никогда не реализовывал поиск по двухфазному имени), либо из GCC (который до недавнего времени не реализовывал поиск по двухфазному имени одинаково).
То, что вы сталкиваетесь с тем, что Visual Studio не реализует двухфазный поиск. Они только ищут фактическое имя, когда вы создаете экземпляр шаблона.
И Microsoft уже почти решила, что не заинтересована в поддержке двухфазного поиска.
Я не знаю, что вам сказать, за исключением того, что я согласен с вами, что это неправильное поведение.
Я думаю, что, вероятно, происходит то, что в случае MSVC компилятор оптимизирует лишний проход за счет того, что в итоге получает знания об определенной позже функции, которую не следует использовать в случае вызовов без шаблонов. Должен признаться, я не понимаю, как GCC/LLVM в итоге получит результаты, которые они делают, так как результаты - это то, что вы ожидаете как исключение, а не правило.
Я думаю, я бы подал это как ошибку на http://bugreport.apple.com/ и http://connect.microsoft.com/ и посмотрел бы, что они говорят?