插入对流的右值引用是否合理有效

Is it reasonably efficient to insert into an rvalue reference to a stream?

本文关键字:是否 有效 引用 对流 插入      更新时间:2023-10-16

我创建了一个自定义流类型,称之为error_stream,它源自std::ostringstream。我还为流制作了一个名为throw_cpp_class的自定义操纵器(throw_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&?(这会调用move构造函数吗?)。这是不是效率低下得可怕?(并不是说我真的在乎,因为这种例外应该很少见)。

使用此代码:

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,因为您必须让编译器知道您希望它再次将该变量视为右值。

这会调用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.
}

这里它正在工作(使用字符串流)

T&&是一个右值引用,但其值类别是引用(左值)的类别,因此它可以存储在左值引用中。在这种情况下也不会调用move/copy构造函数,因为它是通过引用获取/返回的。

我一点也不认为这是低效的,而是对右值引用的常见和惯用用法。