在全局范围内重载std容器上的操作符是不好的风格吗?

Is it bad style to overload operators on std containers in global scope?

本文关键字:操作符 风格 范围内 全局 重载 std      更新时间:2023-10-16

我遇到了这样的问题:

// A.h
#include <vector>
typedef std::vector<unsigned char> Buffer;
Buffer &operator+=(Buffer &a, Buffer const &b);

// B.h
namespace Bar
{
     struct Qux { };
     Qux &operator+=(Qux &a, Qux const &b);
}

// Foo.cpp
#include "A.h"
#include "B.h"      // comment this out, error goes away
namespace Bar
{
    void foo()
    {
        Buffer a, b;
        a += b;       // error
    }
 }

问题(如这里所述)是a += b;无法编译,因为Bar::operator+=(Qux&, Qux const &)隐藏了::operator+=;并且ADL没有找到::operator+,因为在这种情况下ADL只搜索namespace std;

这是棘手的,因为问题只出现在B.h,但B.h显然与Buffer无关。代码不应该中断取决于我是否包含另一个头。

(实际上我只是在更换编译器时才发现这个问题,我之前使用的编译器名称查找错误,并接受了代码)。

我的问题是:在A.h过载只是一个坏主意,因为这个问题?

我现在通过B.hnamespace Bar内做using ::operator+=;来解决这个问题,但这似乎很hacky,有更好的选择吗?

我能想到的最简单、最安全、最可重用的方法是让+=参数中的一个属于操作符命名空间中的类型:

template <typename T>
struct ArgumentRef
{
    ArgumentRef(T& t) : t_(t) { }
    operator T&() { return t_; }
    operator const T&() const { return t_; }
    T& t_;
};
typedef std::vector<unsigned char> Buffer;
Buffer &operator+=(ArgumentRef<Buffer> a, Buffer const &b) { }

也就是说,继承——由于vector中的非虚拟析构函数而在c++圈子中引起争议——在我看来通常是好的,特别是如果您没有在API中公开Buffer以供更广泛的使用,并且没有在设计为拥有和处理基类的代码中使用动态分配的实例。

虽然我不确定在这种情况下操作符重载是否具有不良风格,(你知道,这可能是一个有争议的问题),这似乎是一个"功能范围"的问题,而不是一个"操作符"的问题。即使您将'operator+=()'更改为'Add()',你可能会得到相同的结果。

在这种情况下,操作符重载是否具有糟糕的样式与这个问题无关。