Ответ 1
Использование-декларация действует как обычное объявление: оно скрывает объявления внешней области, но не подавляет зависящий от аргумента поиск (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
, не создавая двусмысленности. Единственный способ - использовать квалифицированное имя.
Как правильно заметил Рихард Смит в комментариях, 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
.