2017-06-03 29 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」問題是部分排序。 –