Почему 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
}
}
Объявление-использование действует как обычное объявление: оно скрывает объявления внешней области, но не подавляет поиск, зависящий от аргумента (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
,
Когда компилятор пытается разрешить f
в f(x)
это находит B::f
так как мы находимся в пространстве имен B
,
Также находит A::f
с помощью зависимый от аргумента поиск поскольку x
это пример X
который определен в пространстве имен A
, Отсюда и двусмысленность.
Объявление с использованием 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
}