为何使用using std::swap可解决swap调用的二义性问题?
C++ swap调用二义性与消除原因解析
问题代码
#include <iostream> namespace N1 { struct A { int x; }; void swap(N1::A& a1, N1::A& a2) noexcept { std::cout << "N1\n"; } } namespace std { template<> void swap(N1::A&, N1::A&) noexcept { std::cout << "std\n"; } } namespace N2 { void swap(N1::A& a1, N1::A& a2) noexcept { std::cout << "N2\n"; } void foo(N1::A& a1, N1::A& a2) { //using std::swap; // or any another template swap swap(a1,a2); } } int main(int argc, char* argv[]) { N1::A a, b; N2::foo(a,b); }
编译错误信息
try4.cpp:28:5: error: call to 'swap' is ambiguous swap(a1,a2); ^~~~ try4.cpp:9:6: note: candidate function void swap(N1::A& a1, N1::A& a2) noexcept { ^ try4.cpp:22:6: note: candidate function void swap(N1::A& a1, N1::A& a2) noexcept { ^ 1 error generated.
问题描述
在N2命名空间的foo函数内直接调用swap(a1,a2)时,编译器提示调用存在二义性,候选函数为N1::swap和N2::swap。但取消注释第27行的using std::swap(或添加任意swap模板)后,二义性消除,且会调用N1::swap,需解释该现象的原因。
原因分析
1. 无using std::swap时的二义性根源
当在N2::foo中直接调用swap(a1,a2)时,编译器会执行两类查找:
- 常规查找:从当前函数作用域向外遍历,找到
N2命名空间下的N2::swap,这是一个参数完全匹配的非模板函数。 - 依赖实参的查找(ADL):C++规定,调用函数时会自动在实参类型所属的命名空间中查找匹配函数。由于
a1、a2是N1::A类型,编译器会在N1命名空间中找到N1::swap,同样是参数完全匹配的非模板函数。
这两个函数的签名完全一致,且均为非模板函数,重载决议无法区分优先级,因此触发二义性错误。
2. 添加using std::swap后的二义性消除
添加using std::swap后,std::swap(模板函数,包含我们特化的版本)被引入foo函数的作用域,此时候选函数变为三个:N1::swap、N2::swap、std::swap<N1::A>(模板特化实例)。
重载决议遵循以下优先级规则:
- 非模板函数优先于模板函数:
std::swap<N1::A>作为模板实例,优先级低于两个非模板的swap函数,会被排除。 - ADL找到的函数优先于常规查找的函数:剩余的
N1::swap和N2::swap中,N1::swap是通过ADL找到的(与实参类型同属N1命名空间),而N2::swap是常规查找得到的。根据C++重载决议的补充规则,当两个非模板函数的匹配度完全相同时,通过ADL找到的函数会被优先选择,因此二义性消除,最终调用N1::swap。
3. 添加任意swap模板的情况
若添加其他任意swap模板(而非std::swap),原理完全一致:模板函数的存在不会改变非模板函数的优先级,但会触发重载决议中对ADL函数的优先级判定,使得N1::swap(ADL找到)优先于N2::swap(常规查找),从而消除二义性。
内容的提问来源于stack exchange,提问作者Art
相关产品推荐
相关产品推荐

