如何将std::vector的operator+=重载限定为仅类A内部可用?
问题:如何仅让结构体A内部使用std::vector的operator+=重载?
我定义了结构体A,并为std::vector重载了operator+=运算符,代码如下:
#include <iostream> #include <algorithm> #include <stdexcept> #include <vector> template <typename T> std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs) { if (lhs.size() != rhs.size()) throw(std::runtime_error("incompatible vectors")); std::transform(rhs.begin(), rhs.end(), lhs.begin(), lhs.begin(), [](const auto& x, const auto& y) { return x + y; }); return lhs; } template <typename T> struct A { std::vector<T> v; A(const T& x) : v(3, x) { } void add (const std::vector<T>& rhs) { v += rhs; } }; int main() { A<int> a(1); std::vector<int> bvec(3, 2); a.add(bvec); return 0; }
这段代码可以正常运行,但我希望把这个operator+=重载隐藏起来,只允许结构体A内部使用。原因是这个模板重载需要放入头文件,如果其他编译单元也定义了std::vector的operator+=,会引发冲突。
我尝试过三种解决方案,但都不满意:
- 将代码放入命名空间:但若存在
using namespace语句仍可能冲突,且命名空间内的其他代码也能访问该重载; - 在
A内部实现私有do_add方法:虽能限制访问,但需要调用do_add而非直观的v += rhs,降低了代码可读性; - 封装
std::vector为自定义类:需要重写大量成员函数,过于繁琐。
请问:能否保留operator+=的直观语法,同时将其限定为仅结构体A内部可使用?
解决方案
可以通过将operator+=作为结构体A的友元函数定义在其内部来实现需求,具体代码如下:
#include <iostream> #include <algorithm> #include <stdexcept> #include <vector> template <typename T> struct A { std::vector<T> v; A(const T& x) : v(3, x) {} // 友元形式定义operator+=,仅在A的作用域内可见 friend std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs) { if (lhs.size() != rhs.size()) throw(std::runtime_error("incompatible vectors")); std::transform(rhs.begin(), rhs.end(), lhs.begin(), lhs.begin(), [](const auto& x, const auto& y) { return x + y; }); return lhs; } void add(const std::vector<T>& rhs) { v += rhs; // 这里会通过ADL找到友元定义的operator+= } }; int main() { A<int> a(1); std::vector<int> bvec(3, 2); a.add(bvec); // 外部无法调用这个operator+=,下面的代码会编译失败 // std::vector<int> vec1(3, 1), vec2(3, 2); // vec1 += vec2; return 0; }
原理说明:
- 友元函数的作用域:将
operator+=定义为A的友元,该函数的名字只会在A的作用域内可见,外部代码无法直接找到这个重载。 - ADL(参数依赖查找):在
A的成员函数add中调用v += rhs时,由于v是std::vector<T>类型,而友元operator+=的参数包含std::vector<T>,编译器会通过ADL在A的作用域内查找这个重载,从而成功调用。 - 冲突避免:由于该重载仅在
A的作用域内可见,其他编译单元的代码无法访问到它,也就不会和其他地方定义的std::vector的operator+=产生冲突。
补充说明:
如果A是模板类,这个友元函数会随A的实例化而生成,且每个T对应的operator+=是独立的,不会互相干扰。外部代码即使尝试调用std::vector的operator+=,也只会找到标准库或其他全局/命名空间内的版本,不会触发我们定义的这个重载。
内容的提问来源于stack exchange,提问作者francesco
相关产品推荐
相关产品推荐

