繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM