[英]Why does std::ostream not compile when used in ternary operator?
#include <iostream>
using namespace std;
int main()
{
std::ostream o(nullptr);
true ? std::ostream(nullptr) : std::ostream(nullptr); // A
true ? std::ostream(nullptr) : o; //B
return 0;
}
我想知道為什么A
編譯正常而B
失敗並顯示錯誤:
prog.cpp: In function ‘int main()’:
prog.cpp:7:33: error: ‘std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ is protected within this context
true ? o : std::ostream(nullptr);
^
所以我找到了這個網站: https : //docs.microsoft.com/zh-cn/cpp/cpp/conditional-operator-q?view=vs-2017 ,它表示三元運算符具有參數時(“參數”是指的那些左,右:
),那么它可能會導致的參數復制,鑄造等..這將使意義,因為std::ostream
有定義的拷貝構造函數protected
。 因此,在A
,三元運算符會獲得相同類型的兩個參數,因此不會進行任何復制,因此沒有問題。 而在B
,三元運算符獲得不同類型的參數,這顯然導致需要復制,而std::ostream
不允許這樣做。 到目前為止,一切似乎還可以。
但是后來我嘗試了這個:
#include <iostream>
using namespace std;
int main()
{
std::ostream o(nullptr);
std::ostream & oRef = o;
std::ostream && oRRef = std::ostream(nullptr);
true ? std::ostream(nullptr) : std::ostream(nullptr); // A
true ? std::ostream(nullptr) : o; //B
true ? std::ostream(nullptr) : oRef; // C
true ? std::ostream(nullptr) : oRRef; // D
true ? std::ostream(nullptr) : std::move(oRRef); // E
return 0;
}
C
, D
和E
也會因類似錯誤而失敗。
所以我有幾個問題。 表達式的類型是什么: std::ostream(nullptr)
? 三元運算符在具有不同類型的情況下會復制其參數,而在具有相同類型的情況下卻從不復制嗎? 我想念或需要知道的其他內容嗎?
該標准包含一些有關如何評估條件表達式的復雜規則([expr.cond])。 但是,我不會在這里引用這些規則,而是要解釋您應該如何考慮它們。
條件表達式的結果可以是lvalue,xvalue或prvalue。 但是必須在編譯時知道其中的哪一個。 (表達式的值類別永遠不能取決於運行時發生的情況)。 很容易看出,如果第二個和第三個表達式都是相同類型的左值,那么結果也可以設為左值,而不必進行復制。 如果第二和第三個表達式是相同類型的prvalues,然后,作為C ++,17卷,復制具有發生任一---類型的prvalue T
表示類型的對象的延遲初始化T
,並且編譯器僅根據條件選擇要傳遞的那兩個prvalue中的哪一個,最終用於初始化對象。
但是,當一個表達式是左值,而另一個表達式是相同類型的prvalue時,則結果必須是prvalue。 如果標准說結果將是左值,那將是不合邏輯的,因為條件可能導致選擇prvalue操作數,並且您無法將prvalue轉換為左值。 但是您可以反過來做。 因此,該標准表示,當一個操作數是左值,而另一個是相同類型的prvalue時,則左值必須進行左值到右值的轉換。 而且,如果您嘗試在std::ostream
對象上進行從左值到右值的轉換,則由於復制構造函數被刪除,程序將std::ostream
。
從而:
o
需要從左值到右值轉換,因此不會編譯。 oRef
是一個左值,因此仍然需要oRef
值到右值的轉換,因此也不會編譯。 oRRef
仍然是左值(因為右值引用的名稱是左值)。 E的情況值得進一步說明。 在C ++ 11中,很明顯,如果一個參數是xvalue,而另一個參數是相同類型的prvalue,則xvalue必須經過(誤稱)從左值到右值的轉換才能產生prvalue。 對於std::ostream
,它使用受保護的move構造函數(因此該程序違反了成員訪問控制)。 在C ++ 17中,可以考慮更改規則,以便不將xvalue轉換為prvalue,而是將prvalue實例化以產生xvalue,從而避免了移動的需要。 但是,這種更改沒有明顯的好處,並且是否是最合理的行為值得懷疑,因此這可能就是為什么不進行更改的原因(如果考慮的話)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.