为什么B :: f不解决歧义,但A :: f呢?

Bel*_*loc 19 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 or B::f
    }
}
Run Code Online (Sandbox Code Playgroud)

AnT*_*AnT 21

using声明充当普通声明:它隐藏外部作用域声明,但不抑制依赖于参数的查找(ADL).

当你这样做时,using B::f你基本上什么都不做.您只需B::f在本地范围内重新声明,无论如何它已经可见.这并不能阻止ADL的发现A::f,这会在A::f和之间产生歧义B::f.

如果这样做using A::f,本地声明A::f隐藏了外部声明B::f.因此B::f不再可见,不再通过不合格的名称查找找到.A::f现在才发现,这意味着不再存在歧义.

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

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

例如,函数指针的初始化不受ADL的限制

void g( A::X x )
{
    void (*pf)(A::X) = &f;
    pf(x);
}
Run Code Online (Sandbox Code Playgroud)

在上面的例子B::f中将被调用.甚至仅仅一对()函数名就足以抑制ADL,即

void g( A::X x )
{
    (f)(x);
}
Run Code Online (Sandbox Code Playgroud)

已经足够打电话了B::f.

  • 您可以通过括起函数名称来抑制呼叫站点的ADL.使用`(f)(x)`修复了歧义. (4认同)

Joh*_*åde 10

当编译器尝试解析ff(x),B::f因为我们在命名空间中B.它还发现A::f使用参数依赖查找,因为它x是一个实例X是在命名空间中定义的A.因此含糊不清.

声明使用B::f没有任何影响,因为我们已经在命名空间中B.

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