如何重载运算符<<为类成员?

How to overload operator<< for a class member?

本文关键字:lt 成员 运算符 何重载 重载      更新时间:2023-10-16

假设我想打印一个类成员,我试图重载该成员的operator<<

#include <iostream>
#include <map>
template <typename K, typename V>
class MyClass {
 public:
  typedef std::map<K, V> MyMapType;
  MyMapType mymap;
  friend std::ostream& operator<<(
      std::ostream& _os, const typename MyClass<K, V>::MyMapType& _map) {
    for (auto p : _map) _os << p.first << std::endl;
  }
};
int main(int argc, char const* argv[]) {
  MyClass<std::string, int> c;
  c.mymap["a"] = 1;
  c.mymap["b"] = 2;
  std::cout << c.mymap << std::endl;
  return 0;
}

但编译器似乎忽略了定义:

error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'MyMapType' (aka
      'map<std::__1::basic_string<char>, int>'))
  std::cout << c.mymap << std::endl;
  ~~~~~~~~~ ^  ~~~~~~~

那么我该如何正确地超载呢?我必须让它成为类中的一个类,还是需要提供std::map的派生?

它只需要在类之外:

template <typename K,typename V>
std::ostream& operator<<(
   std::ostream& _os, 
   const std::map<K,V> _map
) {
   for (auto& p : _map) _os << p.first << "n";
   return _os;
}

这是因为MyMapType只是std::map的别名,所以如果打印MyMapType的实例,则只打印常规的std::map

您的原始示例不起作用,因为您没有MyClass类型的参数,所以在MyClass中找不到函数。只有在类外部声明友元函数或者友元函数具有类类型的参数时,才能在类外部找到友元函数。

很抱歉,@Vaughn Cato,但问题与friend声明无关。无论如何都没有必要,因此应该避免。

@茶洛:"课堂朋友功能"应该是什么?这没有任何意义。

编译器错误消息在此处具有误导性。找不到函数,因为它不存在。您已经定义了一个操作符,左边是MyClass<...>,右边是流和映射。编译器在找不到一个合理的声明之前,应该先抱怨这个毫无意义的声明。

如果您将运算符定义为成员函数,则运算符的左侧参数始终为this:

class A { /*...*/ };
class B { /*...*/ };
class C {
    public:
        A operator << (const B& rhs);
    /*...*/
};
C c;
B b;
A a = c << b;

对于流操作,您希望流对象位于左侧,this位于右侧。理论上可以在流类中指定运算符,但不能在用户类中指定。由于流类来自标准库,所以这不是一个选项。因此,您只需使用外部运算符定义。您可以在内部调用成员函数,以避免声明流运算符friend:

class A {
    public:
        writeToStream(std::ostream& stream) const;
    /*...*/
};
std::ostream& operator << (std::ostream& stream, const A& a) {
    a.writeToStream(stream);
    return stream;
};

这些函数都不必声明为friend。像躲避瘟疫一样躲避friend。只有在极少数非常罕见的情况下才有必要这样做。不要使用它,只要你不能100%确定你的情况就是其中之一。

因此,以下是已更正的完整示例(未经测试,请注意错误(:

#include <iostream>
#include <map>
template <typename K, typename V>
class MyClass {
   public:
      typedef std::map<K, V> MyMapType;
      MyMapType mymap;
};
template <typename K, typename V>
std::ostream& operator<<(std::ostream& stream,
                         const typename MyClass<K, V>::MyMapType& map) {
    for (auto p : map)
        stream << p.first << std::endl;
    return stream;
}

int main(int argc, char const* argv[]) {
    MyClass<std::string, int> c;
    c.mymap["a"] = 1;
    c.mymap["b"] = 2;
    std::cout << c.mymap << std::endl;
    return 0;
}

在您的示例中,您甚至无法访问MyClass<...>中的任何内容。甚至没有一个遥远的理由来宣布它为friend

顺便说一下:std::endl还刷新流。考虑在运算符内部使用"n"