我无法弄清楚为什么以下代码无法编译。语法与我的其他运算符重载相同。是否存在<<<<超载必须结果?如果是这样,为什么?谢谢你的帮助。
这不起作用 -
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
class Test
{
public:
explicit Test(int var):
m_Var(var)
{ }
std::ostream& operator<< (std::ostream& stream)
{
return stream << m_Var;
}
private:
int m_Var;
};
int _tmain(int argc, _TCHAR* argv[])
{
Test temp(5);
std::cout << temp;
return 0;
}
这确实有效 -
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
class Test
{
public:
explicit Test(int var):
m_Var(var)
{ }
friend std::ostream& operator<< (std::ostream& stream, Test& temp);
private:
int m_Var;
};
std::ostream& operator<< (std::ostream& stream, Test& temp)
{
return stream << temp.m_Var;
};
int _tmain(int argc, _TCHAR* argv[])
{
Test temp(5);
std::cout << temp;
return 0;
}
答案 0 :(得分:9)
任何操作员都必须“friend
ed”并不限制。问题是,如果将运算符声明为实例方法,则第一个参数始终强制为类本身的类型。在这种情况下,您需要操作符的第一个参数(左侧)为std::ostream&
类型,因此,您不能使用实例方法来重载它,并且必须使用全局函数。
顺便说一下,根本不需要将声明为单独函数的运算符声明为friend
函数。只要他们只访问论证的friend
成员,他们就可以愉快地工作,而不必成为班级的public
。
答案 1 :(得分:6)
因为第一个表单重载temp << std::cout
。
答案 2 :(得分:4)
以下是流媒体运营商必须成为朋友的根本原因。
拿这段代码:
struct Gizmo
{
ostream& operator<<(ostream& os) const
{
os << 42;
}
};
int main()
{
Gizmo g;
cout << g;
return 0;
}
考虑调用cout << g;
的上下文当编译器编译此函数时,它首先尝试这个:
cout.operator<<(g);
...如果找不到,那么它会在全局命名空间中查找:
operator<<(cout, g);
...如果找不到,则无法编译。
但是当您尝试将流插入运算符实现为Gizmo的成员时,您希望编译器将您的代码解析为:
g.operator<<(cout);
...除非您将代码更改为:
,否则无法执行此操作g << cout;
......这显然不是你想要的。
答案 3 :(得分:3)
当作为成员函数实现时,运算符重载具有隐式的第一个参数,该参数变为 this 。对于流,这是乱序的:流必须先出现。
使用友元运算符简短,简洁,可以防止意外的隐式转换(由于仅通过ADL使用)。如果你想在线外定义它(例如在一个实现.cpp文件中),那么让它调用一个非公共的,可能是虚方法:
struct T {
template<class Ch, class Tr>
friend
std::basic_ostream<Ch, Tr>& operator<<(
std::basic_ostream<Ch, Tr>& s,
T const& v
) {
s << v.stuff; // or call v.output(s), etc.
return s;
}
private:
int stuff = 0; // initialization here is c++0x only
//virtual void output(std::ostream&) const;
//virtual void output(std::wostream&) const;
// etc., or make it a template, but if it's short,
// just put it in the above friend overload
};
奖励积分:将运营商超载成员命名为 this 。 (提示:它们是静态的。)
答案 4 :(得分:3)
当您执行std::cout << temp;
时,这意味着您将<<
运算符应用于std::cout
(因为运算符是左关联的)。如果你想编写一个成员函数的运算符来实现这一点,你必须将<<
运算符重载到std::cout
所属的任何类,这是不可能的,因为这是你无法修改的。
所以你必须编写一个启用它的函数,一种方法是在全局命名空间中重载<<
,它接受两个参数,即流以及要显示给控制台的对象。像这样的东西
std::ostream& operator<< (std::ostream& stream, Test& temp)
现在你可以把它变成朋友。如果你不把它变成朋友,你必须提供getter函数(比如getMVar
),这将为你提供成员Test
类的值。然而,这不是一个好方法。因此,您将不必要地提供getter函数。因此,制作此类运营商朋友通常是一种惯例。
正如前面所指出的那样,你所做的将导致代码被写为temp << std::cout
,这显然不是你想要的。
答案 5 :(得分:0)
Scott Meyers在Effective C ++第2版中总结第19项:区分成员函数,非成员函数和朋友函数:
运营商GT;&GT;和运算符&lt;&lt;从来都不是会员。如果f是运算符&gt;&gt;或运算符&lt;&lt ;,使f成为非成员函数。此外,如果f需要访问C的非公开成员,请成为C的朋友
此声明只是决定是否制作运算符的指导原则&lt;&lt;和运算符&gt;&gt;成员。最好让他们成为非会员。如果你愿意,你可以让他们成员,但如果你这样做,你将被迫写:
temp << std::cout // when calling operator<<
temp >> std::cin // when calling operator>>
您可以通过将对std :: cout的调用更改为上面的表单来实际纠正您的第一段代码。但这种写作方式绝对不自然。
关于运营商(作为非会员)的朋友,如果任一运营商需要访问私人/受保护的数据成员(就像您的情况一样),那么它必须是朋友,因为它在班级外部。