我发现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);
,则不一致性会消失。
哪个编译器是正确的?
答案 0 :(得分:17)
我记得WG21关于此的核心讨论,其中一位Clang开发者通过引用14.7.1p7来捍卫他们的立场
如果重载解析过程可以在不实例化类模板定义的情况下确定要调用的正确函数,则未指定该实例化是否实际发生。
另一方面,对于一个格式错误的程序(这是执行所需实例化时的情况),没有“正确的函数调用”这样的概念,所以我同意另一个人的立场在那次讨论中,谁说他看不出这会让Clang走那条路。
在p7的示例中,它显示了在执行和不执行其他实例化的情况下格式良好的代码。
在任何情况下,即使允许Clang这样做,程序的良好构成也会依赖于特定的偶然事件(未指定的行为)。因此,标准不再要求接受您的计划,老实说,我不知道这意味着什么。我认为这些代码是不正确的。