插入到流的右值引用中是否合理有效?

0xb*_*7ed 5 c++ c++11

我做了一个自定义流类型,调用它error_stream,它源自std::ostringstream.我还为流调用了一个自定义操纵器throw_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.
Run Code Online (Sandbox Code Playgroud)

我发现通过定义一个插入运算符,它将流的右值引用作为第一个操作数,我现在可以这样做:

error_stream() << "some error " << message() << throw_cpp;
Run Code Online (Sandbox Code Playgroud)

插入运算符如下所示:

error_stream& operator<<(error_stream&& s, const throw_cpp_class&)
{
    throw s.str();
    return s;
}
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么我可以返回类型的值error_stream&&,其中一个error_stream&是必需的?(这会调用移动构造函数吗?).这非常低效吗?(不是我真的在乎,因为异常应该是罕见的).

Moo*_*uck 15

使用此代码:

error_stream& operator<<(error_stream&& s, const throw_cpp_class&)
{
    throw s.str();
    return s;
}
Run Code Online (Sandbox Code Playgroud)

你可以error_stream&& s作为a 返回error_stream&,因为它s是一个左值,它不是一个右值.

"什么?" 你问?"但我明白了&&!" 这部分C++很棘手.当您看到type&& s(并且type不是模板)时,这意味着该变量是一个右值引用,它是一个"从"右值构造的引用.但它有一个名字:s.所有名字都是左值.这就是std::move有时必须打电话的原因,因为你必须让编译器知道你希望它再次将该变量视为右值.

这会调用move构造函数吗?).

不,它只是返回对左值的引用s.

这非常低效吗?(不是我真的在乎,因为异常应该是罕见的).

不,因为没有副本,甚至没有动作发生.


与您的实际问题无关,流的大多数重载是:

ostream& operator<<(ostream&& s, const T&)
Run Code Online (Sandbox Code Playgroud)

然后这意味着除非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.
}
Run Code Online (Sandbox Code Playgroud)

它在这里工作(使用stringstream)