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

Why doesn't B::f solve the ambiguity but A::f does?

本文关键字:解决 歧义 不能 为什么      更新时间:2023-10-16

为什么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)。

当你做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类型,因此ADL将始终为不合格名称f找到函数A::f。你不能把它排除在考虑之外。这意味着你不能在不产生歧义的情况下考虑B::f。唯一的方法是使用限定名称

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

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

void g( A::X x )
{
    void (*pf)(A::X) = &f;
    pf(x);
}

在上述示例中,将调用CCD_ 15。即使函数名周围只有一对()也足以抑制ADL,即

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

已经足以使其调用CCD_ 17。

当编译器试图在f(x)中解析f时,它会找到B::f,因为我们在名称空间B中。由于x是在名称空间A中定义的X的实例,因此它还使用参数相关查找来查找A::f。因此产生了歧义。

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

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

您应该每次都显式地编写命名空间。只做

#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
}