Gcc和clang之间的C++不一致

Mat*_*vid 28 c++ gcc clang template-specialization language-lawyer

我碰到一个C++之间不一致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);
}
Run Code Online (Sandbox Code Playgroud)

显然,这段代码并不意味着什么; 它是对Boost Range库中出现的某些东西的一种积极的简化(f简化make_iterator_range).这Bad_Type_Fcn是一个类型函数(技术上,a 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;
Run Code Online (Sandbox Code Playgroud)

似乎正在发生的是clang消除第二次重载f(),可能是(?),因为调用仅使用1个参数,整数42,而第二次重载需要2个参数.另一方面,gcc不消除第二个重载,而是尝试实例化struct Bad_Type_Fcn<int>,这会导致错误.

如果我在调用中删除显式实例化f(),并且(void)f(42);反而写,则不一致性消失.

哪个编译器是正确的?

Joh*_*itb 17

我记得有关WG21的核心讨论,其中一位Clang开发者通过引用14.7.1p7来辩护他们的立场

如果重载解析过程可以在不实例化类模板定义的情况下确定要调用的正确函数,则未指定该实例化是否实际发生.

另一方面,对于一个格式错误的程序(这是执行所需实例化时的情况),没有"正确的调用函数"的概念,所以我同意在讨论中另一个人的立场谁说他看不出这让Clang走那条路.

在p7的示例中,它显示了在执行和不执行其他实例化的情况下格式良好的代码.

无论如何,即使允许Clang这样做,你的程序的良好构成也会依赖于特定的偶然事件(未指明的行为).因此,标准不再要求接受您的计划,老实说,我不知道这意味着什么.我认为这些代码是不正确的.

  • @mmd我认为如果删除显式模板参数,程序就不再格式错误,因为类型推导会失败(匹配错误).参数列表是`(int)`,参数列表是`(const U&,int)`.显式指定模板参数是一个问题,因为在执行`(int)`vs`(const int&,int)`的匹配之前,参数将被替换. (4认同)
  • @dyp如果你没有实例化类模板`Bad_Type_Fcn`,那么你不会引发错误.正如我所说,实例化确实是*必需*因为它是替换过程的一部分,但是14.7.1 [temp.inst]/7使clang不能进行实例化.我认为类型然后保持不完整,然后`bad_Type_Fcn <U> :: type`是一个立即的演绎错误,因为`invalid_class_type :: member`总是失败. (3认同)