据我理解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.1
和gcc 4.7.2
的反应相同。
为什么这种现象似乎只适用于流?直接将Dummy&&
传递给期望T&
无论是否std::move
都失败的函数。
答案 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)
被调用而没有移动时,Stream
将std::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
也是如此,返回类型将匹配。