11

考慮以下最小例如:訂單運算符重載決策涉及的臨時

#include <iostream> 

using namespace std; 

class myostream : public ostream { 
    public: 
     myostream(ostream const &other) : 
      ostream(other.rdbuf()) 
     { } 
}; 

int main() { 
    cout << "hello world" << endl; 

    myostream s(cout); 
    s << "hello world" << endl; 

    myostream(cout) << "hello world" << endl; 
} 

輸出,無論在g ++以及上的Visual C++,是

hello world 
hello world 
0x4012a4 

寫入到臨時對象的版本,myostream(cout),似乎更喜歡成員運營商ostream::operator<<(void *),而不是免費運營商operator<<(ostream &, char *)。無論對象是否有名字,似乎都會產生影響。

爲什麼會發生這種情況?我如何防止這種行爲?

編輯:爲什麼它發生在各種答案中現在很清楚。至於如何防止這種情況,下面看起來有吸引力:

class myostream : public ostream { 
    public: 
     // ... 
     myostream &operator<<(char const *str) { 
      std::operator<<(*this, str); 
      return *this; 
     } 
}; 

但是,這會導致各種含糊之處。

+0

您可能認爲這回答另一個問題爲起點的東西這至少與你想要實現的類似:http://stackoverflow.com/questions/469696/what-is-your-most-useful-cc-snippet/470999#470999你將不得不爲類添加功能接受輸入修飾符(std :: hex,std :: endl ...),但這不應該太難。 – 2010-02-11 16:30:14

回答

6

右值不能綁定到非const引用。因此,在你的例子中,類型ostream的臨時不能是免費運營商< <(std :: ostream &,char const *)的第一個參數,使用的是會員運營商< <(void *)。

如果你需要它,你可以添加一個調用,如

myostream(cout).flush() << "foo"; 

這將改造成右值的參考。

請注意,在C++ 0X中,右值引用的引入將允許提供以rvalue引用作爲參數的運算符< <的超載,從而解決問題的根本原因。

+0

您的意思是「不能綁定rvalues ...」嗎?和「......將右值轉換成左值...」? – 2010-02-11 19:04:56

+0

對,我已經解決了我的迴應。謝謝。 – AProgrammer 2010-02-11 19:27:49

3

我剛剛意識到部分的答案。臨時不是左值,因此它不能用作ostream &類型的參數。

的問題:「我怎樣才能使這項工作」依然存在,

+0

最簡單:提供可以爲你工作的成員函數。 – 2010-02-11 16:31:43

7

如果一個對象沒有一個名字(即它是暫時的),它不能被綁定到一個非const引用。具體來說,它不能被綁定到的第一個參數:

operator<<(ostream &, char *) 
+0

..直到C++ 0x出現,並賦予我們r值引用。 – dirkgently 2010-02-11 17:02:49

-1

好了,我不知道C++規範,導致這一點,但很容易蘇斯了爲什麼它會發生。

臨時存在於堆棧上,通常被傳遞給另一個函數或者調用一個單獨的操作。所以,如果你調用它的免費運營:

操作< <(myostream(COUT))

它在此操作結束和第二個「< <」運營商要追加ENDL將被銷燬引用一個無效的對象。免費的「< <」運算符的返回值將是對被破壞的臨時對象的引用。 C++規範可能定義了有關免費運營商的規則,以防止這種情況令C++程序員感到沮喪和困惑。

現在,在臨時的「<(void *)」成員操作符的情況下,返回值是對象本身,它仍然在堆棧上並且不被銷燬,所以編譯器知道不會銷燬它只是將它傳遞給下一個成員操作員,即接受endl的操作員。運算符在臨時對象上的鏈接對於簡潔的C++代碼來說是一個非常有用的功能,所以我確信C++規範設計者會考慮它並實現編譯器來有意地支持它。

編輯

有人說,它是一個非const引用做。此代碼編譯:

#include <iostream> 
using namespace std; 
class myostream : public ostream { 
    public: 
     myostream(ostream const &other) : 
      ostream(other.rdbuf()) 
     { } 
      ~myostream() { cout << " destructing "; } 
    }; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works"); 
    std::operator << (result, "illegal"); 
     return 0; 
} 

,並返回

This works destructing illegal 
+1

對不起 - 你對臨時工的生活時間的理解是錯誤的。 – 2010-02-11 16:25:59

+0

請隨時給我啓發。 AFAIK,這裏的描述與我的解釋一致:http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr382。 HTM。如果您在臨時使用靜態方法,則在清理該操作的調用堆棧時必須銷燬該臨時對象。如果它被用作成員操作的「this」,那麼它可以在操作完成後繼續存在,因爲它位於調用堆棧的底部,因此鏈接成員操作符將工作,但靜態方法不會。 – 2010-02-11 16:34:55

+0

您發佈的鏈接是一個衆所周知的C++信息不佳來源(並展示奇怪的行爲,將我翻轉到另一頁面)。 C++標準是這裏的參考,並且它(有效地)說,臨時必須一直存在,直到完成表達式的一部分結束,在這種情況下,這是完整的<<運算符鏈。代碼不能按預期工作的真正原因在我的答案中給出。 – 2010-02-11 16:42:47

1

由於沒有一個答案,到目前爲止似乎給了一個乾淨的解決方案,我會滿足於骯髒的解決方案:

myostream operator<<(myostream stream, char const *str) { 
    std::operator<<(stream, str); 
    return stream; 
} 

這是唯一可能的,因爲myostream有一個拷貝構造函數。 (在內部,它是由參考計數std::stringbuf支持的。)

0

由於存在右值引用,所以我認爲這可能是C++ 11之前的解決方法。

解決的辦法是有一個成員函數< <運營商,我們可以轉換爲一個非const引用基類:

class myostream : public ostream { 
    public: 
     // ... 
     template<typename T> 
     ostream &operator<<(const T &t) { 
      //now the first operand is no longer a temporary, 
      //so the non-member operators will overload correctly 
      return static_cast<ostream &>(*this) << t; 
     } 
};