Почему множественные наследуемые функции с одним и тем же именем, но разными сигнатурами не рассматриваются как перегруженные функции? - PullRequest
47 голосов
/ 20 марта 2011

Следующий фрагмент кода выдает ошибку «неоднозначный вызов foo» во время компиляции, и я хотел бы знать, есть ли способ обойти эту проблему без полной квалификации вызова foo:

#include <iostream>

struct Base1{
    void foo(int){
    }
};

struct Base2{
    void foo(float){
    }
};

struct Derived : public Base1, public Base2{
};

int main(){
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;
}

Итак, вопрос, как следует из названия.Идеи?Я имею в виду следующее работает безупречно:

#include <iostream>

struct Base{
    void foo(int){
    }
};

struct Derived : public Base{
    void foo(float){
    }
};

int main(){
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;
}

Ответы [ 3 ]

47 голосов
/ 20 марта 2011

Правила поиска членов определены в Разделе 10.2 / 2

Следующие шаги определяют результат поиска имени в области видимости класса, C.Во-первых, рассматривается каждое объявление имени в классе и в каждом из его подобъектов базового класса.Имя элемента f в одном подобъекте B скрывает имя элемента f в подобъекте A, если A является подобъектом базового класса B. Все скрытые объявления исключаются из рассмотрения .Каждое из этих объявлений, которое было введено с помощью объявления использования, считается от каждого подобъекта C, который имеет тип, содержащий объявление, обозначенное объявлением использования. Если результирующий набор объявлений не все из подобъектов одного типа, или набор имеет нестатический член и включает в себя элементы из разных подобъектов, возникает двусмысленность, и программа плохо сформирована .В противном случае этот набор является результатом поиска.

class A {
public:
  int f(int);

};
class B {
public:
   int f();

};
class C : public A, public B {};
int main()
{
     C c;
     c.f(); // ambiguous
}

Таким образом, вы можете использовать using декларации A::f и B::f для разрешения этой неоднозначности

class C : public A, public B {
     using A::f;
     using B::f;

};

int main()
{
     C c;
     c.f(); // fine
}

Второй код работает безупречно, потому что void foo(float) находится в области видимости Си.На самом деле d.foo(5); вызывает void foo(float), а не int версию.

4 голосов
/ 19 сентября 2016

Поиск имени - это отдельная фаза для разрешения перегрузки .

Поиск имени происходит первым.Это процесс определения того, к какой области относится имя.В этом случае мы должны решить, означает ли d.foo d.D::foo, или d.B1::foo, или d.B2::foo.Правила поиска имени не принимают во внимание параметры функции или что-либо еще;речь идет исключительно об именах и областях.

Только после того, как это решение принято, мы затем выполняем разрешение перегрузки для различных перегрузок функции в области, в которой было найдено имя.

В вашем примере вызов d.foo() нашел бы D::foo(), если бы была такая функция.Но нет ни одного.Таким образом, работая в обратном направлении, он пробует базовые классы.Теперь foo может равным образом выглядеть до B1::foo или B2::foo, так что это неоднозначно.

По той же причине вы получите двусмысленность, вызывая неквалифицированную foo(5); внутри D функции-члена.


Эффект рекомендуемого решения:

struct Derived : public Base1, public Base2{
    using Base1::foo;
    using Base2::foo;

состоит в том, что это создает имя D::foo и идентифицирует две функции.В результате d.foo преобразуется в d.D::foo, и затем разрешение перегрузки может произойти для этих двух функций, которые обозначены D::foo.

Примечание. В этом примере D::foo(int) и Base1::foo(int)два идентификатора для одной функции;но в целом для поиска имен и разрешения перегрузки не имеет значения, являются ли они двумя отдельными функциями или нет.

2 голосов
/ 20 марта 2011

Будет ли у вас работать?

struct Derived : public Base1, public Base2{
   using Base2::foo;}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...