C++ несоответствие между gcc и clang
Я столкнулся с несоответствием C++ между 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 аргументом, целое число 42
в то время как вторая перегрузка требует 2 аргумента. С другой стороны, gcc
не устраняет вторую перегрузку, а вместо этого пытается создать экземпляр struct Bad_Type_Fcn<int>
, что приводит к ошибке.
Несоответствие исчезает, если я удаляю явное создание экземпляра в вызове f()
, и писать (void)f(42);
вместо.
Какой из компиляторов правильный?
1 ответ
Я помню дискуссию в WG21 по этому поводу, и один из разработчиков Clang отстаивал свою позицию, ссылаясь на 14.7.1p7.
Если процесс разрешения перегрузки может определить правильную функцию для вызова без создания экземпляра определения шаблона класса, неизвестно, имеет ли место эта реализация на самом деле.
С другой стороны, для плохо сформированной программы (которая имеет место здесь, когда выполняется требуемая реализация), нет такого понятия "правильная функция для вызова", поэтому я согласен с позицией другого парня в этом обсуждении кто сказал, что не видит, что это позволяет Клангу идти по этому пути.
В примере p7 он показывает код, который правильно сформирован как с дополнительным выполнением, так и без него.
В любом случае, даже если Clang разрешено это делать, правильная форма вашей программы будет зависеть от конкретных случаев (неопределенное поведение). Поэтому стандарт больше не требует, чтобы ваша программа была принята, и, честно говоря, я не знаю, что это значит. Я считаю такой кодекс плохо сформированным.