据我了解 C++11 引用,我不应该能够将右值引用绑定(bind)到(非常量)左值引用,因为前者可能绑定(bind)到临时对象,而后者绝不能绑定(bind)到一个临时的。
但是我发现这种奇怪的行为与临时流对象有关(我尽可能地减少了)
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
一样无法绑定(bind)到左值引用是,但两者都是gcc 4.6.1
和 gcc 4.7.2
react 相同。
为什么这种现象似乎只适用于流?直接传递 Dummy&&
时到一个需要 T&
的函数使用和不使用 std::move
均失败.
最佳答案
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;
}
这在标准中称为“右值流插入”,位于 §27.7.3.9 [ostream.rvalue]。
它允许从右值 basic_ostream
隐式转换(某种)到一个左值。它是专门为允许 temporary streams to be usable without resorting to tricks 引入的.
至于省略move为什么会编译失败:
当 Stream& operator<<(Stream& s, Dummy)
被称为没有 move ,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
来解决这个问题在它自己的行上(它不会被隐式地向上转换。)
move 不是问题,因为您通过了我们刚刚首先发现的右值到左值插入运算符。在该函数内部,流是一个 basic_ostream
所以Stream
也是如此,并且返回类型将匹配。
关于c++ - 为什么我可以 std::move 流右值引用到左值引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14866360/