在嵌套命名空间中重载流操作符

Stream operator overloading in nested namespace

本文关键字:操作符 重载 嵌套 命名空间      更新时间:2023-10-16

最近,当我实现一个类时,我创建了一个名为operator 的嵌套命名空间,在其中添加流操作符。

我这样做是因为我经常需要在类的名称空间之外的名称空间中使用它们。

using my_namespace::operators;

就在我想要的地方,就这样。

这里我有一个例子,一个类Point,一个类Segment和它们的流操作符,其中Segment的流调用Point的流。但是…我不能编译:

类点:

#ifndef POINT_HPP
#define POINT_HPP
#include <iostream>
namespace geom {
class Point
{
public:
    Point(int x_, int y_) : x(x_), y(y_) {};
    int x;
    int y;
 };
namespace operators {
    std::ostream& operator<<(std::ostream& out, const Point& p)
    {
        out << "(" << p.x << ", " << p.y << ")";
        return out;
    }
} // ~ namespace geom::operators
} // ~ namespace geom
#endif // ~ POINT_HPP

类部分:

#ifndef SEGMENT_HPP
#define SEGMENT_HPP
#include <iostream>
#include "point.hpp"
namespace geom_2d {
class Segment
{
public:
    Segment(const geom::Point& a_, const geom::Point& b_) : a(a_), b(b_) {};
    geom::Point a;
    geom::Point b;
};
namespace operators {
    std::ostream& operator<<(std::ostream& out, const Segment& p)
    {
        using namespace geom::operators;
        out << "[" << p.a << ", " << p.b << "]";
        return out;
    }
} // ~ namespace geom_2d::operators
} // ~ namespace geom_2d
#endif // ~ SEGMENT_HPP
主:

#include <iostream>
#include "segment.hpp"
#include "point.hpp"
using namespace geom_2d::operators;
int main()
{
    geom::Point p1(3, 5);
    geom::Point p2(1, 6);
    geom_2d::Segment s(p1, p2);
    std::cout << s << std::endl;
    return 0;
}

这不能编译,我得到:

../segment.hpp:21: error: no match for ‘operator<<’ in ‘std::operator<< [with _Traits = std::char_traits<char>](((std::basic_ostream<char, std::char_traits<char> >&)((std::ostream*)out)), ((const char*)"[")) << p->geom_2d::Segment::a’

如果我删除命名空间操作符编译正确,但正如我告诉你的,我想避免它。

我认为问题与在另一个名称空间操作符中使用名称空间操作符调用有关。

任何想法?

不清楚为什么要将操作符置于与类型不同的名称空间中。一般来说,建议操作符应该与其操作的用户定义类型位于相同的名称空间中。这样做可以启用参数依赖查找,这反过来将帮助您在使用它时找到正确的操作符(并将解决编译错误)。

如果有真正的理由将操作符放在不同的命名空间中,您可以在该命名空间中提供标记类型,然后使用继承强制ADL查看嵌套的命名空间(using指令对ADL没有帮助):

namespace A {
   namespace operators {
      struct tag {};
   }
   struct B : operators::tag {};
   namespace operators {
      std::ostream& operator<<(std::ostream& out, const ::A::B& obj) { ... }
   }
}
namespace C {
   void foo() {
      ::A::B b;
      std::cout << b;
   }
}

注意,这在某种程度上是一种攻击,有些人会惊讶于操作符没有在A命名空间中定义…它之所以有效,是因为类型的关联名称空间集包括定义类型的名称空间以及它的所有基的名称空间(在本例中,由于::A::B::A::operators::tag之间的继承关系,::A::operators被拉出)

:如果你在与类型相同的命名空间中定义操作符,那么根本不需要 using指令,因为ADL会在需要的时候找到它们。

问题是您已经通过using将名称空间导入到代码中,但是库头并没有这样做。因此,它们只能在全局命名空间、命名空间std中找到操作符,或者通过依赖于参数的查找找到操作符。

你可以通过

来解决这个问题
using namespace geom_2d::operators;

之前
#include <iostream>