管理同一对象的多个流运算符(运算符<<,>>)

时间:2013-05-02 09:51:43

标签: c++ namespaces operator-overloading

我希望能够为一个对象定义流操作符(<<>>),但是要有多个它们,以便我可以根据上下文使用不同的集合。例如,如果我有一个如下对象:

struct Foo {
  int a;
};

然后在某些地方我希望能够使用这个

std::ostream& operator<<(std::ostream& out, const Foo& foo) {
  out<<foo.a;
}

而在其他人中,我更喜欢使用此

std::ostream& operator<<(std::ostream& out, const Foo& foo) {
  out<<"'"<<foo.a<<"'";
}

但我无法看到如何同时定义这两者。

我正在提交自己的答案,但我希望其他人过去有理由比我更多地考虑这个问题,并且最好有一个有组织的方式在大型应用程序中这样做(因此,术语“管理“问题标题”。

3 个答案:

答案 0 :(得分:4)

创建一个操纵器:一个简单的代理,它只为替代格式提供一个名称(和一个重载槽)。

// default format
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
  return out<<foo.a;
}

// custom format

// helper class
struct print_quoted_manip {
    Foo const &ref;

    print_quoted_manip( Foo const &in )
        : ref( in ) {}
};

// implementation
std::ostream& operator<<( std::ostream& out, print_quoted_manip o )
    { return out << '\'' << o.ref << '\''; }

// the manipulator
print_quoted_manip print_quoted( Foo const &in )
    { return print_quoted_manip( in ); }

// usage
std::cout << print_quoted( my_foo );

您可以轻松地对此进行模板化以在任何类型的对象周围添加引号。

答案 1 :(得分:2)

虽然您提出的解决方案可行,但我强烈建议您不要这样做。运算符重载是一个很容易被滥用的强大工具。如果行为不是直观且明显,那么它往往弊大于利。

我建议只使用第一个版本

std::ostream& operator<<(std::ostream& out, const Foo& foo) {
  out<<foo.a;
}

并简单地使用

cout << "'" << fooObject << "'";

而不是提供不同的过载。

这使代码清晰直观,并且更易于调试。

我可以想象在代码中不正确使用using namespace会产生许多错误。还有代码重复 - 如果要将另一个成员b添加到对象并打印该对象,该怎么办?其他人可能会发现其中一个重载,修改它,看到它在第一次看起来工作,并忘记其他重载。

答案 2 :(得分:1)

您可以在适当命名的每组运算符周围声明命名空间,然后在需要使用这些特定流运算符的函数中使用该命名空间。例如:

#include <iostream>
#include <string>

struct Foo {
  int a;
};

namespace raw_output {
  std::ostream& operator<<(std::ostream& out, const Foo& foo) {
      out<<foo.a;
  }
}

namespace quoted_output {
  std::ostream& operator<<(std::ostream& out, const Foo& foo) {
      out<<"'"<<foo.a<<"'";
  }
}

void do_raw() {
  using namespace raw_output;
  Foo f = { 7 };
  std::cout<<f<<std::endl;
}

void do_quoted() {
  using namespace quoted_output;
  Foo f = { 7 };
  std::cout<<f<<std::endl;
}

void do_raw_and_quoted() {
  Foo f = { 7 };
  { // Anonymous scoping to use different ops in the same function scope
    using namespace raw_output;
    std::cout<<f<<std::endl;
  }
  {
    using namespace quoted_output;
    std::cout<<f<<std::endl;
  }
}

int main() {
  do_raw();
  do_quoted();
  do_raw_and_quoted();
}

使用这种方法,您应该避免在顶层使用命名空间(在函数范围之外),因为这可能会污染并模糊使用所需的运算符。