[英]Why doesn't this << overload compile
我无法弄清楚为什么以下代码无法编译。 语法与我的其他运算符重载相同。 是否存在限制,必须对“超载”进行限制? 如果是这样,为什么? 谢谢你的帮助。
这不起作用 -
#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;
}
任何操作员都必须“ friend
编辑”并不是限制。 问题是,如果将运算符声明为实例方法,则第一个参数始终强制为类本身的类型。 在这种情况下,您需要运算符的第一个参数(左侧)为std::ostream&
类型std::ostream&
因此,您不能使用实例方法来重载它,并且必须使用全局函数。
顺便说一句,根本不需要将声明为单独函数的运算符声明为friend
函数。 只要他们只访问他们的论点的public
成员,他们就可以愉快地工作而不是班上的friend
。
因为第一个表单重载了temp << std::cout
。
这是流运营商必须成为朋友的根本原因。
拿这个代码:
struct Gizmo
{
ostream& operator<<(ostream& os) const
{
os << 42;
}
};
int main()
{
Gizmo g;
cout << g;
return 0;
}
考虑调用cout << g;
的上下文cout << g;
当编译器编译此函数时,它首先尝试这样做:
cout.operator<<(g);
...如果找不到,那么它在全局命名空间中查找:
operator<<(cout, g);
...如果找不到,则无法编译。
但是当您尝试将流插入运算符实现为Gizmo的成员时,您希望编译器将您的代码解析为:
g.operator<<(cout);
...除非您将代码更改为以下内容,否则无法执行此操作:
g << cout;
......这显然不是你想要的。
当作为成员函数实现时,运算符重载具有隐含的第一个参数, 该参数变为此参数。 对于流,这是乱序的:流必须先到。
使用友元运算符简短,简洁,并且可以防止不期望的隐式转换(由于仅通过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
};
加分点:将操作员命名为没有此操作的超载成员。 (提示:它们是静态的。)
当你做std::cout << temp;
这意味着你将<<
运算符应用于std::cout
(因为运算符是左关联的)。 如果你想编写一个成员函数的运算符来实现这一点,你必须将<<
运算符重载到std::cout
所属的任何类,这是不可能的,因为那是你无法修改的。
所以你必须编写一个启用它的函数,一种方法是在全局命名空间中重载<<
,它接受两个参数,流以及你想要显示给控制台的对象。 这样的事情
std::ostream& operator<< (std::ostream& stream, Test& temp)
现在你可以把它变成朋友。 如果你不把它变成朋友,你必须提供getter函数(比如getMVar
),它将为你提供成员Test
class的值。 然而,这不是一个好方法。 因此,您将不必要地提供getter函数。 因此,制作此类运营商朋友通常是一种惯例。
正如前面所指出的,你正在做的事情会导致代码被写成这个temp << std::cout
,这显然不是你想要的。
正如Scott Meyers在Effective C ++第2版第19项中总结的那样:在成员函数,非成员函数和朋友函数之间进行区分 :
operator >>和operator <<永远不是成员。 如果f是operator >>或operator <<,则使fa成为非成员函数。 此外,如果f需要访问C的非公开成员,请成为C的朋友
此声明只是决定是否创建operator <<和operator >>成员的指南。 最好让他们成为非会员。 如果你愿意,你可以让他们成员,但如果你这样做,你将被迫写:
temp << std::cout // when calling operator<<
temp >> std::cin // when calling operator>>
您可以通过将对std :: cout的调用更改为上面的表单来实际纠正您的第一段代码。 但这种写作方式绝对不自然。
关于运营商(作为非会员)的友情,如果任一运营商需要访问私人/受保护的数据成员(就像您的情况一样),那么它必须是朋友,因为它在班级外部。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.