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

bit*_*ask 5 c++ iostream rvalue-reference move-semantics c++11

据我理解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));
}
Run Code Online (Sandbox Code Playgroud)

如果我写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.

GMa*_*ckG 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;
}
Run Code Online (Sandbox Code Playgroud)

这在标准中称为"Rvalue流插入",位于§27.7.3.9[ostream.rvalue].

它允许从rvalue basic_ostream到lvalue的隐式转换(排序).它专门用于允许临时流可用而无需借助技巧.


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

没有移动的情况下Stream& operator<<(Stream& s, Dummy)调用时,将继承自(即).Streamstd::fstreamstd::ostreambasic_ostream<char>

它将使用basic_ostream<E, T>& operator<<(basic_ostream<E, T>&, const char*)过载插入你的字符串,然后尝试返回表达式的结果,这将是一个ostream.你不能从隐式垂头丧气std::ostream&std::fstream&,所以你得到一个错误.

您可以通过返回s它自己的行来修复它(它不会被隐式地提升.)

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