为什么不这个<<重载编译

时间:2010-01-22 20:05:27

标签: c++ operator-overloading

我无法弄清楚为什么以下代码无法编译。语法与我的其他运算符重载相同。是否存在<<<<超载必须结果?如果是这样,为什么?谢谢你的帮助。

这不起作用 -

#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;
 }

6 个答案:

答案 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的调用更改为上面的表单来实际纠正您的第一段代码。但这种写作方式绝对不自然。

关于运营商(作为非会员)的朋友,如果任一运营商需要访问私人/受保护的数据成员(就像您的情况一样),那么它必须是朋友,因为它在班级外部。