【问题标题】:C++ Defining the << operator of an inner classC++定义内部类的<<操作符
【发布时间】:2014-01-14 08:46:46
【问题描述】:

在一个我没有发起的项目上工作,我想在一个类中添加一个&lt;&lt; 运算符。问题:该类是另一个类的私有内部类,后者在namespace中。

我做不到。

问题可以这样简化:

#include <iostream>
#include <map>
namespace A {
    class B {
        private:
            typedef std::map<int, int> C;
            C a;
            friend std::ostream& operator<<(std::ostream& os, const C &c) {
                for (C::const_iterator p = c.begin(); p != c.end(); ++p)
                    os << (p->first) << "->" << (p->second) << " ";
                return os;
            }
        public:
            B() {
                a[13] = 10;
                std::cout << a << std::endl;
            }
        };
}
int main() {
    A::B c;
}

我尝试用g++ test.cpp 编译它:error: no match for ‘operator&lt;&lt;’。编译器没有找到我的重载函数。我认为在标题中定义它会更简单,但没有运气。如果你觉得比较合适,我也可以在CPP文件中定义类,但是我不知道怎么做。

最后一个要求,我不能使用 C++11(很遗憾)。

【问题讨论】:

  • 您的代码适用于 Visual C++ 编译器版本 15.0(即 VS2008,C++11 之前)。您使用的是哪个编译器?在 ideone.com 上不起作用....
  • 我在那里看不到内部类。只是命名空间中的一个普通类。
  • @TonyD:好问题,我相应地更新了文本。我使用了普通的 g++:gcc 版本 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)。
  • ADL 可以通过这种方式找到typedefs 吗?如果您用新的类定义替换 typedef 怎么办? (仅用于测试)
  • @user980053:不,它不是内部类,它是std::map 的类型别名。所以ADL只考虑namespace std,并没有在namespace A中找到你的操作符。

标签: c++ operator-overloading header-files


【解决方案1】:

由于友元运算符首先在类中声明,因此只能通过依赖于参数的查找获得。但是,它的两个参数类型都不在namespace A 中,所以找不到。 Cstd::map 的别名,因此在 ADL 中被视为在 namespace std 中。

有多种方法可以修复它,但都不是完美的:

  • 在类定义前声明namespace A中的函数;然后它通过正常查找变得可用,而不仅仅是 ADL。但是,这会在一定程度上破坏封装,如果其他任何东西试图将 operator&lt;&lt; 重载为 std::map,可能会导致问题。
  • 用命名的静态(非友元)函数替换运算符重载,并按名称调用它。
  • C 声明为内部类,而不是std::map 的别名。这可以在不破坏封装的情况下启用 ADL,但如果您希望它的行为类似于 std::map,那就有点尴尬了。

【讨论】:

  • 看起来不错!但是我用A::operator&lt;&lt;(std::cout, a); 替换了std::cout &lt;&lt; a &lt;&lt; std::endl;,得到了error: ‘operator&lt;&lt;’ is not a member of ‘A’。我做错了吗?
  • @user980053:对不起,现在想起来也行不通;运算符只能被 ADL 找到。就个人而言,我可能会放弃重载&lt;&lt; 的想法,并编写一个像static void B::print(std::ostream&amp; os, const C &amp;c) 这样的命名函数。
  • @MikeSeymour:当它完全合格时,为什么它需要被任何东西找到?
  • @LightnessRacesinOrbit:我很确定,因为它没有在命名空间中声明,所以它只能被 ADL 找到,即使是完全限定的。但是我对名称查找的理解已经快到极限了,如果我再想一想,我的大脑就会融化。
  • @MikeSeymour:实际上,想一想,这确实敲响了警钟,因为我记得过去对这个荒谬的规则感到震惊和震惊;)我最多会花十分钟在标准中寻找它然后放弃。
【解决方案2】:

根据 Mike Seymour 的回答,这是第一个解决方案的示例。 注意 operator

namespace A {

  // It has to expose the B::C's type
  std::ostream& operator<<(std::ostream& os, const std::map<int, int> &c);

  class B {
  private:
    typedef std::map<int, int> C;
    C a;
    friend std::ostream& operator<<(std::ostream& os, const B::C &c);
  public:
      B() {
        a[13] = 10;
        std::cout << a << std::endl;
      }
    };

  std::ostream& operator<<(std::ostream& os, const B::C &c) {
    for (B::C::const_iterator p = c.begin(); p != c.end(); ++p) {
      os << (p->first) << "->" << (p->second) << " ";
    }
    return os;
  }
}

【讨论】:

  • 酷!唯一的缺点是我现在为namespace A 的每个map&lt;int, int&gt; 定义operator&lt;&lt;。我希望我可以将定义限制为C...
猜你喜欢
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-11
  • 2011-11-08
相关资源
最近更新 更多