[英]Conversion operator vs deleted constructor
請參見以下代碼:
struct X;
struct Y {
Y() {}
Y(X&) = delete;
};
struct X {
X() {}
operator Y() {
return{};
}
};
int main() {
X x;
static_cast<Y>(x);
}
在這里,顯式刪除了使用X
的Y
的構造函數,而X
將轉換運算符轉換為Y
在這兩個直接矛盾的地方,似乎=delete
總是贏; 我在GCC,Clang和VC ++的某些最新版本上進行了測試。
問題:這是“正確的”行為嗎? 我認為轉換構造函數和轉換運算符之間沒有特殊的優先級,因此上面的代碼應產生重載解析度歧義錯誤。 但事實並非如此。 它抱怨刪除功能的使用。 是因為可以保證復制保留嗎?
我用谷歌搜索,發現轉換構造函數與轉換運算符:priority 。 在該問題中,選擇了轉換運算符,因為由於轉換構造const
中存在const
,因此它更匹配。 但是,在我的情況下,將Y(X&)
替換為Y(X const&)
沒有改變。
實際上,我想要的情況如下:
X x;
Y y1(x); // Error
Y y2 = static_cast<Y>(x); // OK
是的,這可能很愚蠢,但是確實有一些內置類型的行為就像這樣: X <- int&
, Y <- int&&
。 無法創建完全模仿內置引用類型的用戶定義類型似乎是當前C ++迫切需要的部分...
從標准11.6.17.6.2
如果初始化是直接初始化,或者如果復制是初始化,則源類型的cv不合格版本與目標的類相同,或者是該類的派生類,則考慮構造函數。
然后標准告訴我們( 11.6.16
)
各種形式的初始化以及
new
表達式(8.5.2.4),static_cast
表達式(8.5.1.9),功能符號類型轉換(8.5.1.3),mem初始化器(15.6.2) ,條件的括號初始列表形式稱為直接初始化。
您的示例通過static_cast
初始化了一個臨時項,因此由於直接初始化,編譯器只允許使用構造函數,因此會出現錯誤。
問題:這是“正確的”行為嗎? 我以為轉換構造函數和轉換運算符之間沒有特別的優先級[...]
那不是很正確。 您正在按原樣查看代碼:
struct Y {
Y() {}
Y(X&) = delete;
};
但實際上還有更多的東西。 對於編譯器, Y
看起來像:
struct Y {
Y() {}
Y(X&) = delete;
Y(Y&&) = default;
Y(Y const&) = default;
};
這里的選擇不在Y(X&)
和X::operator Y()
。 選擇主要在Y(X&)
和Y(Y&&)
。 而且前者比后者更好(不管,正如您在問題中提到的那樣,它都是X&
或X const&
作為參數)。 但是它已被刪除,因此轉換格式錯誤。
如果我們是復制初始化而不是直接初始化:
Y y = x;
那么兩者將是同樣可行的(因此模棱兩可)。 是的,您真的希望這個模棱兩可。 = delete
不會從候選集中刪除!
將構造函數從Y(X&)
更改為Y(X const&)
將具有首選的轉換功能。
是的,這可能很愚蠢,但是確實有一些內置類型的行為就像這樣:
X <- int&
,Y <- int&&
是的,但是在原始示例中, X
和Y
是不同的類型 。 在這里,它們代表相同類型的不同值類別。 這個新示例起作用或不起作用的原因完全不同:
X x;
Y y1(x); // Error
Y y2 = static_cast<Y>(x); // OK
是真的:
int& x = ...;
int&& y(x); // error, can't bind rvalue reference to lvalue
int&& y2 = static_cast<int&&>(x); // ok. this is exactly std::move(x)
與引用兼容類型的引用綁定與轉換優先級的問題不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.