请考虑以下最小示例:
#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;
}
};
然而,这导致各种模糊。
rvalues不能绑定到非const引用。所以在你的例子中,临时类型ostream不能是自由运算符<<(std :: ostream&,char const *)的第一个参数,使用的是成员运算符<<(void *)。
如果需要,可以添加如下调用
myostream(cout).flush() << "foo";
这会将rvalue转换为参考。
请注意,在C ++ 0X中,引入rvalue引用将允许提供操作符<<的重载,将rvalue引用作为参数,解决问题的根本原因。
如果对象没有名称(即它是临时的),则不能绑定到非const引用。具体来说,它不能绑定到第一个参数:
operator<<(ostream &, char *)
我才发现 部分 的答案。临时不是左值,因此不能用作类型的参数 ostream &
。
“我怎样才能做到这一点”的问题仍然存在......
由于到目前为止没有一个答案似乎给出了一个干净的解决方案,我将解决这个肮脏的解决方案:
myostream operator<<(myostream stream, char const *str) {
std::operator<<(stream, str);
return stream;
}
这是唯一可能的原因 myostream
有一个复制构造函数。 (在内部,它由重新计数支持 std::stringbuf
。)
虽然C ++ 11确实解决了这个问题,因为有rvalue引用,我认为这可能是pre-C ++ 11的一种解决方法。
解决方案是有一个成员函数<<运算符,我们可以将其转换为对基类的非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;
}
};
好吧,我不知道导致这种情况的C ++规范,但很容易理解它为什么会发生。
临时存在于堆栈上,通常传递给另一个函数或者在其上调用单个操作。所以,如果你打电话给它的免费运营商:
运营商<<(myostream(COUT))
它在此操作结束时被销毁,并且附加endl的第二个“<<”运算符将引用无效对象。来自自由“<<”运算符的返回值将是对被破坏的临时对象的引用。 C ++规范可能定义了关于自由运算符的规则,以防止这种情况使C ++程序员感到沮丧和困惑。
现在,在临时的“<<(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