如何让std::tuple::operator==复用其他命名空间的operator==?
关于std::tuple比较时operator==查找的问题
问题背景
我使用一个无法修改的C风格结构体Foo,并在foo_ops命名空间中为它实现了独立的operator==。另外有一个包含Foo成员的类Bar,也实现了自己的operator==。原本通过using namespace foo_ops可以正常比较Foo成员,但改成用std::tie把Bar转换成std::tuple进行比较时,编译器找不到foo_ops::operator==——原因是ADL(参数依赖查找)失效了,因为Foo和它的operator==不在同一个命名空间。全局作用域的using foo_ops::operator==能解决问题,但函数内的该声明以及全局的using namespace foo_ops都无效。
提出的问题
- 不使用全局
using声明,能否让std::tuple::operator==选用foo_ops::operator==?是否应该把Foo的operator==移到全局命名空间? - 为什么全局
using namespace foo_ops无效,而using foo_ops::operator==有效?
问题1解答
不使用全局
using声明的话,有两种可行思路:- 针对tuple类型自定义operator==:为
Bar通过std::tie生成的特定tuple类型,手动实现operator==,在里面显式调用foo_ops::operator==比较Foo成员。但这种方案需要匹配tuple的具体类型,扩展性差,不推荐。 - 将operator==移至Foo所在的命名空间:这是最符合C++设计规则的做法。ADL的核心就是在参数类型的原生命名空间中查找运算符,如果
Foo本身在全局命名空间定义,把operator==移到全局后,std::tuple::operator==在比较Foo元素时,ADL会自动找到这个运算符,无需任何额外声明。这完全符合ADL的设计意图,是最优解。
如果实在无法移动
operator==的位置,那只能放弃std::tie的写法,在Bar的operator==中手动拆分比较逻辑:先调用foo_ops::operator==比较Foo成员,再结合其他成员的比较结果,用逻辑与组合起来。虽然代码繁琐,但能避开tuple的ADL问题。- 针对tuple类型自定义operator==:为
问题2解答
这本质是**using namespace和using声明在重载决议中的可见性规则差异**:
std::tuple::operator==定义在std命名空间内,当它需要比较Foo元素时,会触发ADL查找Foo所在命名空间的运算符,同时会查找当前上下文可见的运算符。但全局的using namespace foo_ops只是让foo_ops内的名字在全局作用域可见,这种可见性不会被跨命名空间继承到std命名空间的函数上下文里——也就是说,std::tuple::operator==的内部查找不会感知到全局using namespace带来的名字。- 而全局的
using foo_ops::operator==是直接将这个运算符的声明注入到全局作用域,成为全局重载集合的一部分。此时,任何需要比较Foo的上下文(包括std::tuple::operator==触发的运算符查找),都会把全局作用域的这个operator==纳入重载决议,自然就能被匹配到。
内容的提问来源于stack exchange,提问作者jamesdlin
相关产品推荐
相关产品推荐

