Почему B :: f не решает неоднозначность, а 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
}
}

19

Решение

Объявление-использование действует как обычное объявление: оно скрывает объявления внешней области, но не подавляет поиск, зависящий от аргумента (ADL).

Когда вы делаете using B::f ты вообще ничего не меняешь. Вы просто redeclare 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);
}

В приведенном выше примере B::f будет называться. И даже просто пара () вокруг имени функции достаточно для подавления ADL, т.е.

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

уже достаточно, чтобы позвонить B::f,

21

Другие решения

Когда компилятор пытается разрешить f в f(x) это находит B::f так как мы находимся в пространстве имен B,
Также находит A::f с помощью зависимый от аргумента поиск поскольку x это пример X который определен в пространстве имен A, Отсюда и двусмысленность.

Объявление с использованием B::f не имеет никаких эффектов, так как мы уже находимся в пространстве имен B,

Чтобы устранить неоднозначность, используйте A::f(x) или же B::f(x),

10

Вы должны писать пространство имен каждый раз явно. Просто делать

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