19

为什么 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
    }
}
4

3 回答 3

21

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

当你这样做时,using B::f你基本上什么都没有改变。您只需B::f在本地范围内重新声明,无论如何它已经可见。这并不妨碍 ADL 也能找到,这会在和A::f之间产生歧义。A::fB::f

如果这样做using A::f,则 的本地声明会A::f隐藏 的外部声明B::f。所以B::f不再可见,也不再通过不合格的名称查找找到。现在只A::f找到了,这意味着不再有歧义。

无法抑制 ADL。由于您的案例中的参数是A::X类型A::f的,因此 ADL 将始终为 unqualified name 找到function 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

于 2013-07-06T17:48:25.157 回答
10

当编译器尝试解析f时,f(x)它发现B::f因为我们在命名空间中B。它还找到A::fusing参数相关查找,因为x它的实例X是在命名空间中定义的A。因此模棱两可。

B::f由于我们已经在命名空间中,因此声明 using没有任何效果B

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

于 2013-07-06T17:49:47.450 回答
1

您应该每次都明确地编写命名空间。做就是了

#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
}
于 2013-07-06T17:48:38.160 回答