2017-06-03 26 views
8

gcc 8.0.0clang 5.0.0不同意这个程序的行为:的转发参考,并扣除正常参考部分订购指南

#include <iostream> 

template <typename T> 
struct A { 
    A(const T&) { std::cout << __PRETTY_FUNCTION__ << '\n'; } 
    A(T&&)  { std::cout << __PRETTY_FUNCTION__ << '\n'; } 
}; 

template <typename U> A(U&&) -> A<double>; 

int main() { 
    int i = 0; 
    const int ci = 0; 

    A a1(0); // both say A<double> 
    A a2(i); // both say A<double> 
    A a3(ci); // gcc says A<int>, clang says A<double> 
} 

GCC的行为没有任何意义,我 - 如果const T&超载最好的U&&过载为左值const int,为什么不是T&&过载首选到U&&过载对于右值int?铿锵声对我来说更有意义(没有一个功能比其他功能更专业,所以演绎指南获胜)。

谁是对的?

+0

我会对此进行一次刺探。扣除是针对一个右值(参考)。 'ci'是'const'的左值,碰巧有一个构造函数将'const'的引用作为参数。 –

+0

请原谅我缺乏的知识,但是这个' - >'语法是如何调用的? – HolyBlackCat

回答

7

我们又在偏序地。该类型的合成功能模板参数是

T&&  // #1: not a forwarding reference 
const T& // #2 
U&&  // #3: a forwarding reference 

预偏序变换strips away referenceness,之后的top-level cv-qualification,留给我们一个裸露型在所有三种情况。因此,在所有三种情况下,演绎在两个方向都能成功。现在我们留下[temp.deduct.partial]/9的决胜局:

如果,对于给定类型,扣除在两个方向成功(即,上面的转换后的 类型是相同的)和两个P和A 分别引用类型(之前被替换为称为 上述类型):

  • 如果从参数模板的类型是一个左值 参考和从参数模板的类型没有,则 参数类型不被认为是至少和一样专业参数类型;否则,
  • 如果参数模板 中的类型比参数模板中的类型(如上面描述的 )更具cv限定,则该参数类型不会被认为至少为 ,与参数类型相同。

对于U&& VS T&&,既不规则适用,而且也没有订货。但是,对于U&&const T&,参数类型U&&不像第一个项目符号中的参数类型const T&那样特定。

部分排序因此发现#2比#3更专业化,但发现#1和#3无法区分。 GCC是正确的。

这就是说,这可能是部分排序规则中的疏忽。类模板扣除是我们第一次有一个“右值引用cv-unqualified模板参数不是转发引用”的东西。以前,在双引用情况下,转发引用将始终会丢失到第二个项目符号中的非转发右值引用(因为只有对于某些非空cvcv T&&)才能获得非转发右值引用。

+0

这一切都有道理。根据最后一段,你是否认为第一个子弹也应该包含非转发右值引用(即,宁愿将'T &&'放在'U &&'上)? – Barry

+0

Iirc,超负荷解决方案具有联络断路器子弹,它涉及部分排序和检查两个候选人中的一个是否是推导指南。看来GCC和clang以不同的顺序应用这些领带。我不认为这是关于部分排序的不一致,但我目前在android上,所以我不能研究。 –

+0

@ JohannesSchaub-litb你的意思是Clang没有实现[P0620](https://wg21.link/P0620)?也许。但是,“#2为什么不#1击败#3”问题是部分排序。 –