C++ 为什么';t B::f解决了歧义,但A::f解决了?

C++ 为什么';t B::f解决了歧义,但A::f解决了?,c++,argument-dependent-lookup,C++,Argument Dependent Lookup,为什么B::f不能解决歧义,而A::f可以 namespace A { class X { }; void f( X ); } namespace B { void f( A::X ); void g( A::X x ) { using B::f; // which expression shall I use here to select B::f? f(x); // ambiguous A::f

为什么B::f不能解决歧义,而A::f可以

namespace A
{
    class X { };
    void f( X );
} 

namespace B
{
    void f( A::X );
    void g( A::X x )
    {
        using B::f;   // which expression shall I use here to select B::f?
        f(x);         // ambiguous A::f or B::f
    }
}

using声明充当普通声明:它隐藏外部范围声明,但不抑制参数相关查找(ADL)

当你使用B::f
时,你基本上什么都没有改变。您只需在本地范围内重新声明
B::f
,它在本地范围内已经可见。这并不妨碍ADL找到
A::f
,这会在
A::f
B::f
之间产生歧义

如果使用A::f
执行
,则
A::f
的本地声明将隐藏
B::f
的外部声明。因此,
B::f
不再可见,也不再通过非限定名称查找找到。现在只找到了
A::f
,这意味着不再存在歧义

不可能抑制ADL。由于您案例中的参数是
A::X
类型,ADL将始终为非限定名称
f
找到函数
A::f
。你不能把它排除在考虑之外。这意味着您不能在不产生歧义的情况下考虑
B::f
。唯一的解决方法是使用限定名称

正如@Richard Smith在评论中正确指出的,ADL可以被抑制。ADL仅在函数名本身用作函数调用中的后缀表达式时使用。以任何其他方式指定目标函数都会欺骗ADL

例如,函数指针的初始化不受ADL的约束

void g( A::X x )
{
    void (*pf)(A::X) = &f;
    pf(x);
}
在上面的示例中,将调用
B::f
。而且,即使在函数名周围仅仅有一对
()
,也足以抑制ADL,即

void g( A::X x )
{
    (f)(x);
}

已经足够让它调用
B::f

您应该每次都显式地编写名称空间。照办

#include <iostream>

namespace A
{
    class X { };
    void f( X ) {
        std::cout << "A";
    }
} 

namespace B
{
    void f( A::X ) {
        std::cout << "B";
    }
    void g( A::X x )
    {
        // using B::f;
        B::f(x);        
    }
}

int main() {
    B::g(A::X()); // outputs B
}
#包括
名称空间A
{
类X{};
空隙f(X){

std::cout当编译器试图在
f(x)
中解析
f
时,它会找到
B::f
,因为我们在命名空间
B
中。 它还使用查找
A::f
,因为
x
x
的一个实例,该实例在命名空间
A
中定义。因此存在歧义

使用
B::f
的声明没有任何效果,因为我们已经在命名空间
B


要解决歧义,请使用
A::f(x)
B::f(x)

您可以通过将函数名插入括号来抑制调用位置的ADL。使用
(f)(x)
修复了歧义。显然,在上述任何代码摘录中都不需要
使用B::f;
,而且永远不要
使用名称空间xxx
-它只会模糊并导致讨厌的错误(甚至不需要
使用名称空间std
:)@slashmais,这当然是真的。但在这么小的片段中,我认为这是允许的。最重要的是不要习惯它-如果你习惯了,你会忘记在真正的代码中不要这样写。是的-修复了!