2017-10-14 219 views
7

我有一個類C,它有一個任何東西的鑄造操作符。在這個例子中,我嘗試用三種不同的方法將它的一個實例轉換爲std::stringstatic_caststd::string的構造函數並指定爲std::string。然而,只有最後一個編譯,而其他人提出了一個模棱兩可的構造函數的錯誤。顯式強制轉換,直接初始化和複製初始化之間的不同行爲

錯誤的原因很清楚:有很多方法可以將C轉換爲std::string的構造函數可以接受的東西。但這些情況之間有什麼區別?爲什麼要讓操作員按照預期工作,但不在那裏?

struct C { 
    template<typename T> 
    operator T() const { 
     return T{}; 
    } 
}; 

int main() { 
    C c; 

    cout << static_cast<string>(c) << endl; // compile error 
    string bad(c); // compile error 
    string ok = c; // compiles successfully 
} 

UPD:如在評論中提到bolov,這個問題不會與C++ 17重現。我用g ++-5和clang-3.8用-std = C++ 11和-std = C++ 14對它進行了測試,它顯示了所描述的錯誤。

+0

無法重現:https://godbolt.org/g/ESR8cw – bolov

+0

@bolov很奇怪,但它並沒有重現C++ 17。它在godbolt中用-std = C++ 11重現。我會將其添加到帖子中,謝謝。 –

+0

hmm ..有趣 – bolov

回答

6

之前C++ 17

static_cast<string>(c)string bad(c)執行direct initialization,然後

T構造函數被檢查和最佳匹配是由過載分辨率來選擇。然後調用構造函數來初始化該對象。

正如你所說的,所有的std::string可能的構造進行檢查和C可以轉換爲需要什麼,然後產生歧義。

string ok = c執行copy initialization(注意這不是分配),然後

如果T是一個類類型和other類型的CV-不合格的版本不是TT衍生,或者如果T是非班級類型,但other的類型是類別類型,用戶定義的轉換序列可以從other類型轉換爲T(如果TT類型,並且轉換函數是可用)進行檢查a nd最好的選擇是通過重載解析。

這意味着將檢查從Cstd::string的轉換,並將其用於初始化。

後C++ 17

由於C++ 17 direct initlizatioin

如果初始化是prvalue表達式,其CV-非限定類型是相同的類T,初始化表達式本身,而不是臨時實體化,用於初始化目標對象:請參見copy elision(自C++ 17以來)

這意味着從Cstd::string的轉換被優先使用並用於初始化,然後歧義消失,代碼運行良好。

LIVE

+1

詳細說明覆制初始化([over.ics.user]/3):*如果用戶定義的轉換由專用的轉換函數模板指定,則第二個標準轉換序列應具有完全匹配的等級。我不會說這是最好的。 – chris