Проблема разрешения перегрузки C++

У меня есть следующая структура:

struct  A
{
    A();
    virtual ~A();
    
    virtual void    Foo() =0;
};

struct  E;
struct  F;

struct  B:  public A
{
    B();
    virtual ~B();
    
    virtual void    Bar(E*) =0;
    virtual void    Bar(F*) =0;
};

struct  C:  public B
{
    C();
    virtual ~C();
    
    void    Bar(E*);
};

struct  D:  public C
{
    D();
    virtual ~D();
    
    void    Foo();
    void    Bar(F*);
};

struct  E:  public A
{
    E();
    virtual ~E();
    
    void    Foo();
    /* ... */
};

struct  F:  public A
{
    F();
    virtual ~F();
    
    void    Foo();
    /* ... */
};

template <class _Base>
struct  G:  public _Base
{
    G(const _Base &b)
    :   _Base(b)
    {}

    virtual ~G()
    {}
    
    using _Base::Bar; // doesn't help
    /* ... */
};

Когда я пытаюсь вызвать Bar() для объекта типа G‹D› с E*, я получаю следующую ошибку времени компиляции:

ошибка: нет соответствующей функции для вызова 'G‹D›::Bar(E*&)'

примечание: кандидаты: виртуальная пустота D::Bar(F*)

Если я переименую объявления (виртуального) void Bar(F*), код компилируется нормально и работает как положено.

Использование:

typedef std::list<E*> EList;
typedef std::list<F*> FList;
EList es;
FList fs;

G<D> player(D());

es.push_back(new E); // times many
fs.push_back(new F); // times many

for(EList::iterator i0(es.begin()), i1(es.end()); i0 != i1; ++i0)
{
  player.Bar(*i0);
}

for(FList::iterator i0(fs.begin()), i1(fs.end()); i0 != i1; ++i0)
{
  player.Bar(*i0);
}

1. Что не так с несколькими перегрузками функций-членов, принимающих разные аргументы?

2. Почему компилятор не может отличить их друг от друга?


person zyndor    schedule 13.10.2009    source источник
comment
Оказывается, проблема связана с разрешением перегрузки, а не с динамической отправкой. Я исправил вопрос. Также нашел это: stackoverflow.com/questions/72010/c-overload-resolution   -  person zyndor    schedule 14.10.2009


Ответы (2)


Только версии Bar в наиболее производном классе, содержащие переопределение Bar, будут рассматриваться для разрешения перегрузки, если вы не добавите объявления using. Если вы пытаетесь

struct  D:      public C
{
    D();
    virtual ~D();

    void        Foo();
    void        Bar(F*);
    using C::Bar;
};

тогда это должно работать.

person Troubadour    schedule 13.10.2009

Из вашего кода:

  • G расширяет D в G<D>
  • вы вызываете Bar(E*) на G -> G не имеет метода Bar, поэтому загляните в базовый класс
  • базовый класс D
  • D имеет Bar(F*), но не Bar(E*) --> struct E отличается от struct F, поэтому вы получаете сообщение об ошибке

Чтобы ответить на ваш вопрос: E не связан типом с F, и компиляция может определить разницу, поэтому вы получаете сообщение об ошибке.

Я не уверен, какой Bar вы добавляете виртуальный, но если базовый класс уже объявляет Bar как виртуальный, все классы, которые его расширяют, уже имеют виртуальный Bar, поэтому не имеет значения, добавляете ли вы слово (виртуальный) в расширенные классы.

Было бы полезно, если бы вы показали, как вы создаете экземпляр своего объекта и как вы вызываете для него Bar(F*). Существуют решения во время выполнения, которые зависят от того, как вы вызываете метод и какие параметры вы передаете.

person stefanB    schedule 13.10.2009
comment
Если вы переопределяете одну версию перегруженных функций базового класса и не хотите скрывать другую, вы должны сделать все видимым с помощью объявления using. - person UncleBens; 14.10.2009
comment
добавил пример использования и исправил ошибку: E и F не являются связанными типами. к логике: D есть C, а C имеет Bar(E*). разве это не считается? не объявляя Bar(E*) и Bar(F*) виртуальными в C и D, я только хотел указать, что их поведение не будет / не будет дальнейшей специализации. они используются через указатели на их объекты суперкласса, поэтому он по-прежнему останется виртуальным вызовом как таковым. - person zyndor; 14.10.2009