У меня есть следующий код:

struct CommonVariables
{/*some common variables that need to be proceed*/};

struct CommonHandler
{
    static void foo(const CommonVariables& vars) {/*processed vars*/}
    void bar(const CommonVariables& vars) {/*processed vars*/}
};

struct AnotherVariables
{/*another variables*/};

struct AnotherHandler
{
    static void foo(const AnotherVariables& vars) {/*processed vars*/}
    void bar(const AnotherVariables& vars) {/*processed vars*/}
};

struct Derived : CommonHandler, AnotherHandler
{};

И когда я пытаюсь вызвать Derived :: foo (/ любой тип переменной /) или Derived d; d.bar (/ любой тип переменной /), компилятор выдает мне ошибку: "ссылка на 'foo (или bar)' неоднозначна".

Еще ниже у меня почти такая же ситуация:

struct DifferentHandler
{
static void foo(const CommonVariables& vars) {}
static void foo(const AnotherVariables& vars) {}
void bar(const AnotherVariables& vars) {}
void bar(const CommonVariables& vars) {}
};

И вызов DifferentHandler :: foo (/ любого типа переменной /) или 'bar' работает просто отлично.

Существует множество решений для обхода первого блока кода (черты шаблона и т. д.). Что я конкретно хочу, это перегрузить методы с помощью наследования. И я не понимаю, почему вызовы из Derived неоднозначны (поскольку входные параметры имеют разные типы, с точки зрения компилятора эти функции разные. Как в DifferentHandler)

ПРИМЕЧАНИЕ: я пробовал MinGW5.8 (в Qt Creator) и MSVC2015 в VS2015. Оба компилятора сгенерировали одну и ту же ошибку.

0
Стас Крутько 24 Апр 2017 в 22:44

2 ответа

Лучший ответ

Ну вот:

struct Derived : CommonHandler, AnotherHandler
{
    using CommonHandler::foo;
    using CommonHandler::bar;
    using AnotherHandler::foo;
    using AnotherHandler::bar;
};

Исправляет вашу проблему.

Причина, по которой оригинальный код не работает, находится в стандарте C ++. Вот интересные цитаты:

В 10/2:

Говорят, что члены базового класса наследуются производным классом. Унаследованные члены могут упоминаться в выражениях так же, как и другие члены производного класса, если только их имена не являются скрытыми или неоднозначными (10.2).

Продолжаем чтение до 10.2:

Поиск имени члена определяет значение имени (id-выражения) в области видимости класса (3.3.7). Поиск имени может привести к неоднозначности, в этом случае программа плохо сформирована ....

... состоит из двух наборов компонентов: набор объявлений, набор членов named f ...

Если имя перегруженной функции найдено однозначно, разрешение перегрузки (13.3) также имеет место перед контролем доступа

В основном, он сначала идет по имени, а позже применяется для разрешения. Представление их производному классу через объявление using помещает их все в область действия производного класса, где вступают в силу нормальные правила разрешения.

3
SergeyA 24 Апр 2017 в 20:22

В C ++, если функция-член производного класса имеет то же имя, что и функция-член базового класса, тогда версия производного класса скрывает базовую версию - даже если сигнатуры функций отличаются, и в противном случае вы ожидаете, что они перегружены. (Если функция-член базового класса является виртуальной, то сигнатура производного класса должна точно соответствовать *, или она (обычно непреднамеренно) скрывает версию базового класса - по этой причине C ++ 11 добавил override псевдо -ключевое слово).

Например:

struct base {
    void foo(std::string s) {}
};

struct derived : base {
    void foo(int i) {}
};

int main() {
    derived d;
    d.foo("hello"); // Error -- cannot convert const char* to int
}

Решение состоит в том, чтобы использовать директиву using, чтобы перевести функцию-член базового класса в область видимости производного класса, т.е.

struct derived : base {
    using base::foo;
    void foo(int i) {}
};

(Альтернативой является использование неясного

derived d;
d.base::foo("hello"); // works

Синтаксис, чтобы указать, что вы хотите вызвать версию базового класса, но это редко встречается в реальном мире.)

0
Tristan Brindle 24 Апр 2017 в 20:31