【发布时间】:2014-04-11 01:41:23
【问题描述】:
我遇到了gcc(版本4.8.1、4.8.2)和clang(版本3.3、3.4)之间的C++不一致。我想知道哪个是正确的。这是程序:
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),它不应该被实例化,因为对于任何 T,Empty<T>::type 从不存在。这个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);,则不一致会消失。
哪个编译器是正确的?
【问题讨论】:
-
GCC 不应该在给定的代码示例中实例化第二个重载。
-
你可能想在 gcc 4.9 上尝试一下;这真的很有趣,特别是因为据我所知,这应该是一个 SFINAE 案例。非常有趣的测试用例,并且大大减少了它。
-
+1,但术语更正(在这样的问题中,它可能很重要):不涉及函数模板 specialisation (因为它们不存在部分专业化) .您有两个 distinct 函数模板,它们恰好具有相同的名称,因此彼此 overload。
-
@MatthieuM.:这花了几天时间,你可以想象...
-
我认为编译器根本不应该实例化#2,但似乎是 GCC 的情况,并且它无法编译,因为它不是 SFINAE 失败。 SFINAE 不适用于
Bad_Type_Fcn体内的typedef typename Empty< T >::type type。如果Empty<T>::type不存在,您需要使嵌套的typetypedef 消失,SFINAE 才能工作。
标签: c++ gcc clang language-lawyer template-specialization