为什么我可以std ::将流rvalue ref移动到左值ref?

时间:2013-02-14 01:26:34

标签: c++ c++11 iostream move-semantics rvalue-reference

据我理解C ++ 11引用,我不能将rvalue引用绑定到(非const)左值引用,因为前者可能绑定到临时,后者必须永远不会绑定到暂时的。

然而,我发现这个奇怪的行为与临时流对象(我尽可能减少)

struct Dummy {};
template <typename Stream>
Stream& operator<<(Stream& s, Dummy) {
  return s << ".";          // <- (A)
}

template <typename Stream>
void pass(Stream&& s) {
  std::move(s) << Dummy();  // <- (X)   rvalue->lvalue conversion?
}

#include <fstream>
int main() {
  pass(std::fstream("test",std::ios::out));
}

如果我在行s << Dummy()中写(X),C ++会在(A)行投诉,说

  

error: invalid initialization of reference of type ‘std::basic_fstream<char>&’ from expression of type ‘std::basic_ostream<char>’

但是,为什么代码(如上所示)编译并按预期工作std::move返回的右值引用应该无法绑定到左值引用,因为表达式s是,但gcc 4.6.1gcc 4.7.2的反应相同。

为什么这种现象似乎只适用于流?直接将Dummy&&传递给期望T&无论是否std::move都失败的函数。

1 个答案:

答案 0 :(得分:10)

basic_ostream的重载次数为operator<<,如下所示:

template <typename Elem, typename Traits, typename T>
basic_ostream<Elem, Traits>&
    operator<<(basic_ostream<Elem, Traits>&& sink, const T& val)
{
    return sink << val;
}

这在标准中称为“Rvalue stream insertion”,见§27.7.3.9[ostream.rvalue]。

它允许从右值basic_ostream到左值的隐式转换(排序)。它是专门为了temporary streams to be usable without resorting to tricks而引入的。


为什么在省略移动时编译失败:

Stream& operator<<(Stream& s, Dummy)被调用而没有移动时,Streamstd::fstream继承自std::ostream(即basic_ostream<char>

它将使用basic_ostream<E, T>& operator<<(basic_ostream<E, T>&, const char*)重载来插入您的字符串,然后尝试返回该表达式的结果,该结果将是ostream 。您不能隐式地从std::ostream&转发到std::fstream&,因此您会收到错误。

你可以通过在它自己的行上返回s来解决这个问题(它不会被隐式上传。)

这不是 with move的问题,因为你经历了我们刚刚发现的rvalue-to -valval插入算子。在该函数内部,流是basic_ostream,因此Stream也是如此,返回类型将匹配。