5

error_streamから派生したカスタム ストリーム タイプを作成し、それを と呼びますstd::ostringstreamthrow_cpp_class(throw_cppは のインスタンスです)というストリーム用のカスタム マニピュレータも作成しましたthrow_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&必要なタイプの値を返すことができるのはなぜですか? (これは移動コンストラクターを呼び出しますか?)。これはひどく非効率的ですか?(例外はまれであるため、私は本当に気にしません)。

4

2 に答える 2

15

このコードで:

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

左辺値であり、右辺値ではないため、error_stream&& sとして返すことができます。error_stream&s

"何?" あなたが尋ねる?「しかし、私は&&そこに見えます!」. C++ のこの部分は注意が必要です。表示されている場合type&& s(およびtypeテンプレートではない場合)、それは変数が右辺値参照であることを意味します。これは、右辺値から「構築された」参照です。しかし、それには名前があります: s. そして、名前を持つものはすべて左辺値です。その変数を再び右辺値として扱いたいことをコンパイラーに知らせる必要があるため、時々呼び出す必要があるのはそのためです。std::move

これは移動コンストラクターを呼び出しますか?)。

いいえ、単に lvalue への参照を返します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.
}

これが機能しています(stringstreamを使用)

于 2013-06-25T01:20:29.630 に答える
3

T&&は右辺値参照ですが、その値カテゴリは参照 (左辺値) のカテゴリであるため、左辺値参照内に格納できます。この場合も、参照によって取得/返されるため、移動/コピー コンストラクターは呼び出されません。

これが少しでも効率が悪いとは言いませんが、右辺値参照の一般的で慣用的な使い方です。

于 2013-06-25T01:19:40.163 に答える