在命名空间中定义的类如何重载输出运算符(<<)

4
考虑一下 bar.h
#include <iostream>

namespace foo {

class Bar {
 public:
  friend std::ostream& operator <<(std::ostream& output, const Bar&);
 private:
  int xx_;
};

}

考虑一下 bar.cc 文件:
#include "bar.h"

std::ostream& operator<<(std::ostream& output, const foo::Bar &in) {

  output << in.xx_ << std::endl;

  return output;
}

为什么不能从Bar类的实现中访问私有成员变量operator <<?即:

$ icpc -c bar.cc
bar.cc(5): error #308: member "foo::Bar::xx_" (declared at line 9 of "bar.h") is inaccessible
    output << in.xx_ << std::endl;
                 ^

compilation aborted for bar.cc (code 2)

我通过将 operator << 的实现嵌入到 foo 命名空间中来解决了这个问题,即:

namespace foo {
std::ostream& operator<<(std::ostream& output, const foo::Bar &in) {

  output << in.xx_ << std::endl;

  return output;
}
}

然而...这是解决问题的正确方法吗?这种方法是否泄露了实现细节?


1
你也可以在定义中写std::ostream& foo::operator<<(...,而不是在命名空间块中。这样做的好处是,如果它与预先声明的函数不匹配,会产生编译错误。 - M.M
2个回答

8

问题:

这是解决问题的正确方法吗?

答案:

是的,这是解决问题的正确方法。

问题:

这种方法不是暴露了实现细节吗?

答案:

完全没有。

该行

friend std::ostream& operator <<(std::ostream& output, const Bar&);

在定义类的命名空间中声明函数为外部函数。如果类没有在命名空间中定义,则该函数将被声明为全局命名空间中的外部函数。由于该函数被声明为 foo 命名空间中的外部函数,因此必须在该命名空间中定义。

如果您希望该函数成为 foo 命名空间之外的全局函数,则必须使用以下代码:

namespace foo
{
    class Bar;
}

std::ostream& operator <<(std::ostream& output, const foo::Bar&);

namespace foo {

   class Bar {
      public:
         friend std::ostream& operator <<(std::ostream& output, const Bar&);
      private:
         int xx_;
   };
}

3
因为你的类Bar是foo命名空间的一部分,所以需要在相同的命名空间内定义函数。
如果你担心隐藏实现细节,你仍可以在bar.cc中定义该函数。
bar.cc
#include "foo.h"

namespace foo
{

std::ostream& operator<<(std::ostream& output, const Bar &in)
{
        output << "Bar x=" << in.x << std::endl;

        return output;
}

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接