I came across a C++ inconsistency between gcc
(versions 4.8.1
, 4.8.2
) and clang
(versions 3.3
, 3.4
). I wonder which one is correct. Here's the program:
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);
}
Clearly, this code is not meant to do anything; it is an aggressive simplification of something that appears in the Boost Range library (with f
simplifying make_iterator_range
). The Bad_Type_Fcn
is a type function (technically, a struct
) which should never be instantiated, because Empty<T>::type
never exists, for any T
. The presence of this struct
and of the second template specialization of f()
is not an error in itself. IRL, f()
provides some functionality for certain types for which Bad_Type_Fcn
is not empty. However that is not the concern here, which is why I simplified those out. I still want f()
to work for types where Bad_Type_Fcn
is empty.
I'm compiling with {g++|clang++} [-std=c++0x] -pedantic -Wall -Wextra -c
. The language standard selection doesn't seem to make a difference. With clang
, the program compiles without errors or warnings. With gcc
, I get an error:
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;
What seems to be happening is that clang
eliminates the second overload of f()
, probably(?) on the basis that the call is made with 1 argument only, integer 42
, while the second overload requires 2 arguments. On the other hand, gcc
doesn't eliminate the second overload, and instead tries to instantiate struct Bad_Type_Fcn<int>
, which results in an error.
The inconsistency disappears if I remove the explicit instantiation in the call to f()
, and write (void)f(42);
instead.
Which of the compilers is correct?