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

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

Следующий фрагмент создает ошибку "ambigious call to 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;
}
4b9b3361

Ответ 1

Правила поиска членов определены в Разделе 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) находится внутри области C. На самом деле d.foo(5); вызывает void foo(float), а не версию int.

Ответ 2

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

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

Ответ 3

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

Сначала начинается поиск имени. Это процесс определения того, к какой области относится это имя. В этом случае мы должны решить, означает ли 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) - два идентификатора для одной функции; но в целом для процесса поиска имен и обработки перегрузки не имеет значения, являются ли они двумя отдельными функциями или нет.