2013-06-25 33 views
5

我做了一個自定義流類型,稱之爲error_stream,它來自std::ostringstream。我還爲流稱爲throw_cpp_classthrow_cppthrow_cpp_class的實例)創建了自定義操縱器。我的目標是有這個語法:插入到流的右值引用是否合理高效?

error_stream s; 
s << "some error " << message() << throw_cpp; // throw_cpp throws an exception containing contents of the stream. 

我發現,通過定義插入運算符,它需要一個右值參考流作爲第一個操作數,我現在可以這樣做:

error_stream() << "some error " << message() << throw_cpp; 

的插入運算符如下所示:

error_stream& operator<<(error_stream&& s, const throw_cpp_class&) 
{ 
    throw s.str(); 
    return s; 
} 

這是怎麼回事?爲什麼我可以返回error_stream&&類型的值,其中error_stream&是必需的? (這是否調用移動構造函數?)。這是非常低效的嗎? (不是我真的很在意,因爲這個例外應該很少見)。

+0

這實際上是一種整潔......我想我可能更喜歡'throw_cpp ',但是要拋出該錯誤與流的內容。 –

+1

我會放棄返回類型和'return os'語句。寫'<<某個錯誤'<< message()<< throw_cpp <<「更多」;''是一個語義錯誤。通過返回'void',它也會變成一個語法錯誤,編譯器會抓住它。 – MSalters

+0

@ MSalters - 好點。我做了改變。 – 0xbe5077ed

回答

15

有了這個代碼:

error_stream& operator<<(error_stream&& s, const throw_cpp_class&) 
{ 
    throw s.str(); 
    return s; 
} 

您可以返回error_stream&& serror_stream&,因爲s左值,它是不是一個右值。

「什麼?」你問? 「但是我看到&&!」。這部分C++是棘手的。當你看到type&& s(而type不是一個模板)時,那意味着該變量是一個右值引用,它是一個「從右值構造」的引用。但它有一個名字:s。所有名稱都是左值。這是爲什麼有時你必須調用std::move,因爲你必須讓編譯器知道你希望它再次將該變量作爲右值處理。

這是否調用移動構造函數?)。

不,它只是返回左值的參考s

這是非常低效的嗎? (不是我真的在乎,因爲這個例外應該很少見)。

沒有,因爲沒有複製,甚至沒有任何舉動發生。


無關你的實際問題,爲流最重載是:

ostream& operator<<(ostream&& s, const T&) 

那麼這意味着,除非throw_cpp是流的第一件事,你的過載將不會被調用,因爲以前的事會流返回ostream&,而不是error_stream&&。 (注意它們應該是模板,但很多不是,並且與點無關),您必須將其重新設置爲error_stream

另外,這不是操縱器的工作方式。機械手是功能,當你流的功能流,流調用函數,並傳遞本身作爲參數,所以你想要的東西更像是這樣:

template <class exception, class charT, class traits> 
std::basic_ostream<charT,traits>& throw_cpp(std::basic_ostream<charT,traits>& os) 
{ 
    error_stream& self = dynamic_cast<error_stream&>(os); //maybe throws std::bad_cast 
    throw exception(self.str()); 
    return os; //redundant, but the compiler might not know that. 
} 

Here it is at work (with stringstream)

+0

這是* short *版本?我不想看到長版... –

+0

哈!好點子。這基本上只是其他答案的鏈接。我刪除了這句話,現在這個答案實際上比鏈接的更長。 –

3

T&&是一個rvalue參考,但它的值類別是基準(左值)的,因此它可以被存儲在左值引用的內部。這種情況下也不會調用移動/複製構造函數,因爲它是通過引用獲取/返回的。

我不會說這樣做效率低下,而是右值引用的常見和習慣用法。