考虑以下最小例子:
#include <iostream>
using namespace std;
class myostream : public ostream {
public:
myostream(ostream const &other) :
ostream(other.rdbuf())
{ }
};
int main() {
cout << "hello world" << endl;
myostream s(cout);
s << "hello world" << endl;
myostream(cout) << "hello world" << endl;
}
g ++和Visual C ++上的输出都是
hello world
hello world
0x4012a4
写入临时对象myostream(cout)
的版本似乎更喜欢成员运算符ostream::operator<<(void *)
,而不是自由运算符operator<<(ostream &, char *)
。无论对象是否有名称,似乎都有所不同。
为什么会这样?我该如何防止这种行为?
修改:为什么会发生这种情况现在可以从各种答案中清楚地看出来。至于如何防止这种情况,以下似乎很有吸引力:
class myostream : public ostream {
public:
// ...
myostream &operator<<(char const *str) {
std::operator<<(*this, str);
return *this;
}
};
但是,这会导致各种歧义。
答案 0 :(得分:7)
如果对象没有名称(即它是临时的),则不能绑定到非const引用。具体来说,它不能绑定到第一个参数:
operator<<(ostream &, char *)
答案 1 :(得分:6)
rvalues不能绑定到非const引用。因此,在您的示例中,类型ostream的临时类型不能是自由运算符&lt;&lt;(std :: ostream&amp;,char const *)的第一个参数,而使用的是成员运算符&lt;&lt;(void *)。< / p>
如果需要,可以添加
等电话myostream(cout).flush() << "foo";
将rvalue转换为引用。
请注意,在C ++ 0X中,引入rvalue引用将允许提供运算符的重载&lt;&lt;将右值引用作为参数,解决问题的根本原因。
答案 2 :(得分:3)
我刚刚实现了答案的部分。临时值不是左值,因此不能用作ostream &
类型的参数。
“我怎样才能做到这一点”的问题仍然存在......
答案 3 :(得分:1)
由于到目前为止没有一个答案似乎给出了一个干净的解决方案,我将解决这个肮脏的解决方案:
myostream operator<<(myostream stream, char const *str) {
std::operator<<(stream, str);
return stream;
}
这是唯一可能的,因为myostream
有一个复制构造函数。 (在内部,它由重新计算的std::stringbuf
支持。)
答案 4 :(得分:0)
虽然C ++ 11确实解决了这个问题,因为有rvalue引用,我认为这可能是预处理C ++ 11的一种解决方法。
解决方案是具有成员函数&lt;&lt;我们可以转换为基类的非const引用的运算符:
class myostream : public ostream {
public:
// ...
template<typename T>
ostream &operator<<(const T &t) {
//now the first operand is no longer a temporary,
//so the non-member operators will overload correctly
return static_cast<ostream &>(*this) << t;
}
};
答案 5 :(得分:-1)
好吧,我不知道导致这种情况的C ++规范,但很容易理解它为什么会发生。
临时存在于堆栈上,通常要传递给另一个函数或者在其上调用一个操作。所以,如果你打电话给它的自由运算符:
运营商LT;≤(myostream(COUT))
它在此操作结束时被销毁,第二个“&lt;&lt;”附加endl的运算符将引用无效对象。来自免费“&lt;&lt;”的返回值运算符将是对被破坏的临时对象的引用。 C ++规范可能定义了关于自由运算符的规则,以防止这种情况使C ++程序员感到沮丧和困惑。
现在,在临时的“&lt;&lt;(void *)”成员运算符的情况下,返回值是对象本身,它仍然在堆栈上而不会被销毁,因此编译器不知道如何破坏它,但将它传递给下一个成员运算符,即运行endl的运算符。运算符链接临时代码对于简洁的C ++代码来说是一个很有用的功能,因此我确信C ++规范设计人员会考虑它并实现编译器以有意支持它。
修改强>
有些人说它与非const引用有关。此代码编译:
#include <iostream>
using namespace std;
class myostream : public ostream {
public:
myostream(ostream const &other) :
ostream(other.rdbuf())
{ }
~myostream() { cout << " destructing "; }
};
int _tmain(int argc, _TCHAR* argv[])
{
basic_ostream<char>& result = std::operator << (myostream(cout), "This works");
std::operator << (result, "illegal");
return 0;
}
它返回
This works destructing illegal