1

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

我这样做是因为我经常需要在类的命名空间操作系统以外的命名空间中使用它们

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’

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

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

有任何想法吗?

4

2 回答 2

5

尚不清楚为什么您希望运算符与您的类型存在于不同的命名空间中。一般来说,建议操作员应该与他们操作的用户定义类型位于相同的命名空间中。这样做会启用 Argument Dependent Lookup,这反过来将有助于在您使用它时找到正确的运算符(并将解决您的编译错误)。

如果确实有理由将运算符放在不同的命名空间中,您可以在该命名空间中提供一个标记类型,然后使用继承来强制 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;
   }
}

请注意,这在某种程度上是一种 hack,有些人会惊讶于运算符未在A命名空间中定义......它之所以有效,是因为类型的关联命名空间集包括定义类型的命名空间以及命名空间它的所有基础(在这种情况下,::A::operators由于 and 之间的继承关系而被::A::B拉出::A::operators::tag

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

于 2013-04-02T15:23:10.023 回答
2

问题是您已经通过 将命名空间导入到代码中using,但库头文件并没有这样做。所以他们只能在全局命名空间、命名空间std或通过参数相关的查找中找到运算符。

你可以通过这样做来解决这个问题

using namespace geom_2d::operators;

#include <iostream>

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

于 2013-04-02T15:25:44.440 回答