Неквалифицированный вызов функции выбирает функцию из неправильного пространства имен - PullRequest
2 голосов
/ 11 июля 2020

Рассмотрим этот код:

#include <iostream>

namespace A {
    struct Mine {};

    template <typename T1, typename T2>
    void foo(T1, T2)
    {
        std::cout << "A::foo" << std::endl;
    }
}

namespace B {
    template <typename T>
    void foo(T, T)
    {
        std::cout << "B::foo" << std::endl;
    }
}   

using namespace A;
using namespace B;
// or with the same effect:
//using A::foo;
//using B::foo;

int main()
{
    A::Mine a;
    foo(a, a);
}

Программа печатает B::foo вместо A::foo. Почему он использует B::foo вместо A::foo?

Представьте себе следующую ситуацию: ваша библиотека предоставляет пространство имен A, а какая-то другая библиотека предоставляет пространство имен B, которое включает шаблон функции с тем же именем foo в качестве вашей библиотеки. Обычно проблем не возникает, поскольку правильную версию можно выбрать с помощью квалифицированных вызовов или на основе поиска, зависящего от аргументов. Но если пользователь вводит и A::foo, и B::foo в одну и ту же область действия, используя объявление using, неквалифицированный вызов не будет неоднозначным, но может быть выбрана неправильная функция.

Есть ли способ предпочесть A::foo over B::foo, поскольку в соответствии с зависимым от аргументов поиском следует выбрать A::foo? Что бы вы посоветовали в этой ситуации?

1 Ответ

4 голосов
/ 11 июля 2020

Он действительно делает правильные вещи. Вот причина.

namespace A {
    struct Mine {};

    template <typename T1, typename T2>
    void foo(T1, T2)
    {
        std::cout << "A::foo" << std::endl;
    }
}

Приведенный выше код будет сопоставлен, если будет передано 2 параметра разного (или одного и того же) типа. Потому что вы определили и T1, и T2 по-разному (но они также могут быть одинаковыми). Но вызов функции

foo(a, a);

имеет оба параметра одного типа. Теперь метод foo с двумя параметрами одного типа определен в namespace B, как показано ниже

namespace B {
    template <typename T>
    void foo(T, T)
    {
        std::cout << "B::foo" << std::endl;
    }
}

И, таким образом, метод из namespace B совпадает, поскольку обе подписи различны.

Попробуйте добавить приведенный ниже код

namespace C {
    template <typename T>
    void foo(T, T)
    {
        std::cout << "B::foo" << std::endl;
    }
}

, и вы получите ошибку компилятора, указав двусмысленность в определении метода, а затем вам придется вручную разрешить область foo, используя что-то например, A::foo(a,a) или любое другое пространство имен, которое вы используете sh.

...