流嵌入式命名空间中的运算符重载

Dim*_*los 1 c++ namespaces operator-overloading stream-operators

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

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

using my_namespace::operators;
Run Code Online (Sandbox Code Playgroud)

就在我想要的地方,就是这样.

这里我有一个类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
Run Code Online (Sandbox Code Playgroud)

类别细分:

#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
Run Code Online (Sandbox Code Playgroud)

主要:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这无法编译,我得到:

../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’
Run Code Online (Sandbox Code Playgroud)

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

我认为问题与在另一个名称空间运算符中使用名称空间运算符进行调用有关.

有任何想法吗?

Dav*_*eas 5

目前还不清楚为什么你希望运算符生活在与你的类型不同的命名空间中.通常,建议操作符应与其操作的用户定义类型位于相同的命名空间中.这样做可以启用Argument Dependent Lookup,这反过来将有助于在您使用它时找到正确的运算符(并将解决您的编译错误).

如果有一个真正的理由让操作符在不同的命名空间中,您可以在该命名空间中提供标记类型,然后使用继承来强制ADL查看嵌套的命名空间(using-directive对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;
   }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这在某种程度上是一种黑客行为,有些人会惊讶于操作符未在A命名空间中定义...它的工作原理是因为类型的关联命名空间集包括定义类型的命名空间以及命名空间所有它的基础(在这种情况下,::A::operators由于::A::B和之间的继承关系被拉::A::operators::tag)

注意:如果在与类型相同的命名空间中定义运算符,则根本不需要using-directive,因为ADL会在需要时找到它们.