簡體   English   中英

為什么在三元運算符中使用std :: ostream不能編譯?

[英]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;
}

CDE也會因類似錯誤而失敗。

所以我有幾個問題。 表達式的類型是什么: std::ostream(nullptr) 三元運算符在具有不同類型的情況下會復制其參數,而在具有相同類型的情況下卻從不復制嗎? 我想念或需要知道的其他內容嗎?

該標准包含一些有關如何評估條件表達式的復雜規則([expr.cond])。 但是,我不會在這里引用這些規則,而是要解釋您應該如何考慮它們。

條件表達式的結果可以是lvalue,xvalue或prvalue。 但是必須在編譯時知道其中的哪一個。 (表達式的值類別永遠不能取決於運行時發生的情況)。 很容易看出,如果第二個和第三個表達式都是相同類型的左值,那么結果也可以設為左值,而不必進行復制。 如果第二和第三個表達式是相同類型的prvalues,然后,作為C ++,17卷,復制具有發生任一---類型的prvalue T表示類型的對象的延遲初始化T ,並且編譯器僅根據條件選擇要傳遞的那兩個prvalue中的哪一個,最終用於初始化對象。

但是,當一個表達式是左值,而另一個表達式是相同類型的prvalue時,則結果必須是prvalue。 如果標准說結果將是左值,那將是不合邏輯的,因為條件可能導致選擇prvalue操作數,並且您無法將prvalue轉換為左值。 但是您可以反過來做。 因此,該標准表示,當一個操作數是左值,而另一個是相同類型的prvalue時,則左值必須進行左值到右值的轉換。 而且,如果您嘗試在std::ostream對象上進行從左值到右值的轉換,則由於復制構造函數被刪除,程序將std::ostream

從而:

  • 在A中,兩個操作數都是prvalue,因此不存在從左值到右值的轉換。 這在C ++ 17中是可以的(但在C ++ 14中不是)。
  • 在B中, o需要從左值到右值轉換,因此不會編譯。
  • 在C中, oRef是一個左值,因此仍然需要oRef值到右值的轉換,因此也不會編譯。
  • 在D中, oRRef仍然是左值(因為右值引用的名稱是左值)。
  • 在E中,一個參數是prvalue,一個參數是xvalue。 xvalue仍需要轉換為prvalue才能使結果成為prvalue。

E的情況值得進一步說明。 在C ++ 11中,很明顯,如果一個參數是xvalue,而另一個參數是相同類型的prvalue,則xvalue必須經過(誤稱)從左值到右值的轉換才能產生prvalue。 對於std::ostream ,它使用受保護的move構造函數(因此該程序違反了成員訪問控制)。 在C ++ 17中,可以考慮更改規則,以便不將xvalue轉換為prvalue,而是將prvalue實例化以產生xvalue,從而避免了移動的需要。 但是,這種更改沒有明顯的好處,並且是否是最合理的行為值得懷疑,因此這可能就是為什么不進行更改的原因(如果考慮的話)。

為什么 ostream iomanip 在傳遞給 operator&lt; 時不需要模板參數<!--?</div--><div id="text_translate"><p> 在 gnu stdc++ header ostream 的命名空間 std 中,這是 std::endl 的定義:</p><pre> template&lt;typename _CharT, typename _Traits&gt; inline basic_ostream&lt;_CharT, _Traits&gt;&amp; endl(basic_ostream&lt;_CharT, _Traits&gt;&amp; __os) { return flush(__os.put(__os.widen('\n'))); }</pre><p> 為什么可以寫std::cout &lt;&lt; std::endl; 在沒有模板參數的情況下使用endl ?</p></div>

[英]Why does an ostream iomanip not need template parameters when passed to operator<<?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM