我遇到了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
),由于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);
。
哪些编译器正确?
我记得对此进行了WG21核心讨论,其中一位Clang开发人员通过引用14.7.1p7
来捍卫自己的位置如果过载分辨率过程可以确定要拨打的正确函数而无需实例化类模板定义,则未指定该实例化是否实际发生。
另一方面,对于一个不形式的程序(在进行必需的实例化时,情况就是这种情况),没有这样的"正确函数"的想法,所以我同意另一个人的位置在那个讨论中,谁说他看不到这允许clang走那条路线。
在P7的示例中,它显示的代码在有或不进行其他实例化的情况下都具有很好的形式。
在任何情况下,即使允许Clang这样做,您程序的良好形式也将依赖于特定的偶然性(未指定的行为)。因此,该标准不再要求您的程序被接受,老实说,我不知道这意味着什么。我认为这样的代码是不构建的。