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

时间:2013-04-02 15:13:51

标签: c++ namespaces operator-overloading stream-operators

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

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

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’

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

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

有什么想法吗?

2 个答案:

答案 0 :(得分:5)

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

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

请注意,这是一个黑客攻击,有些人会惊讶于A命名空间中没有定义运算符...它的工作原理是因为类型的关联命名空间集合包括命名空间定义了类型以及所有类型的名称空间(在这种情况下,::A::operators由于::A::B::A::operators::tag之间的继承关系而被拉出

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

答案 1 :(得分:2)

问题是您已通过using将命名空间导入到代码中,但库标题不会执行此操作。因此,他们只在全局命名空间,命名空间std或通过参数依赖查找中找到运算符。

你可以通过

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

#include <iostream>

但在我看来这似乎是一个糟糕的解决方案。