Подтвердить что ты не робот

С++ несогласованность между gcc и clang

Я столкнулся с несоответствием С++ между 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);.

Какой из компиляторов прав?

4b9b3361

Ответ 1

Я помню основную дискуссию WG21 об этом, и один из разработчиков Clang защитил свою позицию, сославшись на 14.7.1p7

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

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

В примере p7 он показывает код, который хорошо сформирован как с добавлением, так и без него.

В любом случае, даже если Клану разрешено это делать, корректность вашей программы тогда будет опираться на конкретные события (неопределенное поведение). Поэтому Стандарт больше не требует, чтобы ваша программа была принята, и, честно говоря, я не знаю, что это значит. Я считаю такой код плохо сформированным.