Подтвердить что ты не робот

Почему 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
    }
}
4b9b3361

Ответ 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.

Ответ 2

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

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

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

Ответ 3

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

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