Я столкнулся с несоответствием С++ между gcc (версии 4.8.1, 4.8.2) и clang (версии 3.3, 3.4). Интересно, какой из них правильный. Здесь программа:
template < typename T > struct Result {};
template < typename T > struct Empty {};
template < typename T >
struct Bad_Type_Fcn {
typedef typename Empty< T >::type type;
};
template < typename T >
Result< T >
f( const T& ) {
return Result< T >();
}
template< class U >
Result< typename Bad_Type_Fcn< U >::type >
f( const U&, int ) {
return Result< typename Bad_Type_Fcn< U >::type >();
}
int main() {
(void)f< int >(42);
}
Очевидно, что этот код не предназначен для чего-либо; это агрессивное упрощение того, что появляется в библиотеке Boost Range (с f упрощением make_iterator_range). Bad_Type_Fcn - это функция типа (технически, struct), которая никогда не должна создаваться, потому что Empty<T>::type никогда не существует для любого T. Наличие этого struct и второй специализированной специализации f() само по себе не является ошибкой. IRL, f() предоставляет некоторые функции для определенных типов, для которых Bad_Type_Fcn не является пустым. Однако это не вызывает беспокойства, поэтому я упростил их. Я все еще хочу, чтобы f() работал для типов, где Bad_Type_Fcn пуст.
Я компилирую с {g++|clang++} [-std=c++0x] -pedantic -Wall -Wextra -c. Выбор стандарта языка, похоже, не имеет значения. С помощью clang программа компилируется без ошибок или предупреждений. С gcc я получаю сообщение об ошибке:
weird.cpp: In instantiation of ‘struct Bad_Type_Fcn<int>’:
weird.cpp:17:5: required by substitution of ‘template<class U> Result<typename Bad_Type_Fcn<T>::type> f(const U&, int) [with U = int]’
weird.cpp:22:26: required from here
weird.cpp:6:43: error: no type named ‘type’ in ‘struct Empty<int>’
typedef typename Empty< T >::type type;
Что происходит, так это то, что clang устраняет вторую перегрузку f(), возможно (?), исходя из того, что вызов выполняется только с 1 аргументом, integer 42, а для второй перегрузки требуется 2 аргументы. С другой стороны, gcc не устраняет вторую перегрузку и вместо этого пытается создать экземпляр struct Bad_Type_Fcn<int>, что приводит к ошибке.
Неисправность исчезает, если я удалю явное инстанцирование в вызове f() и вместо этого напишу (void)f(42);.
Какой из компиляторов прав?