Переопределение нескольких унаследованных шаблонных функций специализированными версиями

Хорошо, сначала пример кода; это моя попытка сообщить, что я пытаюсь сделать, хотя она не компилируется:

#include <iostream>

template <class T>
class Base
{
public:
    virtual void my_callback() = 0;
};

class Derived1
    : public Base<int>
    , public Base<float>
{
public:
    void my_callback<int>()
    {
        cout << "Int callback for Derived1.\n";
    }
    void my_callback<float>()
    {
        cout << "Float callback for Derived\n";
    }
};

class Derived2
    : public Base<int>
    , public Base<float>
{

public:
    void my_callback<int>()
    {
        cout << "Int callback for Derived2.\n";
    }
    void my_callback<float>()
    {
        cout << "Float callback for Derived2\n";
    }

};

int main()
{
    {
        Derived1 d;
        Base<int> * i_p = d;
        Base<float> * i_f = d;

        i_p->my_callback();
        i_f->my_callback();
    }
    {
        Derived2 d;
        Base<int> * i_p = d;
        Base<float> * i_f = d;

        i_p->my_callback();
        i_f->my_callback();
    }

    //Desired output:
    // Int callback for Derived1.
    // Float callback for Derived1
    // Int callback for Derived2.
    // Float callback for Derived2
    system("Pause");
}

Итак, что я пытаюсь сделать, так это создать своего рода класс-оболочку для наследования, который будет автоматически подключать производный класс к различным спискам обратного вызова; ему нужно подключить конкретный экземпляр производного класса к списку, и я хочу, чтобы «пользователь» имел/получил возможность выполнять функции обратного вызова как часть создания производного класса, как вы можете видеть.

Похоже, это должно работать, хотя мне может понадобиться использовать другой синтаксис. Если это не сработает, есть ли у вас какие-либо предложения?


person Narfanator    schedule 13.12.2009    source источник
comment
Похожие вопросы (но без шаблонов): stackoverflow.com/q/5481356/94687 , stackoverflow.com/q/3150310/94687   -  person imz -- Ivan Zakharyaschev    schedule 09.06.2021


Ответы (4)


Да, вы можете сделать эту работу:

#include <iostream>
using namespace std;

template <class T>
class Base
{
public:
  virtual void my_callback() = 0;
};

class Derived1 : public Base<int>, public Base<float>
{
public:
  void Base<int>::my_callback() {
    cout << "Int callback for Derived1.\n";
  }
  void Base<float>::my_callback() {
    cout << "Float callback for Derived\n";
  }
};

class Derived2 : public Base<int>, public Base<float>
{
public:
  void Base<int>::my_callback() {
    cout << "Int callback for Derived2.\n";
  }
  void Base<float>::my_callback() {
    cout << "Float callback for Derived2\n";
  }
};

int main()
{
  {
    Derived1 d;
    Base<int> * i_p = &d;
    Base<float> * i_f = &d;
    i_p->my_callback();
    i_f->my_callback();
  }
  {
    Derived2 d;
    Base<int> * i_p = &d;
    Base<float> * i_f = &d;
    i_p->my_callback();
    i_f->my_callback();
  }
}

Вывод:

Int callback for Derived1.
Float callback for Derived
Int callback for Derived2.
Float callback for Derived2
person Hans Passant    schedule 13.12.2009
comment
Это не компилируется (gcc). Ваши объявления Derived::my_callback не могут так работать. - person Frunsi; 13.12.2009
comment
Это работает для меня; следует отметить, что я использую Visual Studio 2008, в которой, как я знаю, есть некоторые нестандартные функции. Спасибо! - person Narfanator; 14.12.2009
comment
Да, это побочный эффект поддержки явной реализации интерфейса. С вами все будет в порядке, пока вы можете придерживаться MSVC. - person Hans Passant; 14.12.2009
comment
Спасибо, но он предпочел бы иметь стандартный способ сделать это. Все еще думаю, как это сделать стандартным образом. - person Viet; 27.07.2012

То, что вы хотите, невозможно.

Вы можете добавить специализации шаблонов, хотя я не знаю, действительно ли это помогает:

template <class T>
class Base {
public:
  virtual void my_callback() = 0;
};

template <>
class Base<int> {
public:
  virtual void my_callback() {
    cout << "Base<int>::my_callback()\n";
  }
};

template <>
class Base<float> {
public:
  virtual void my_callback() {
    cout << "Base<float>::my_callback()\n";
  }
};

class Derived1 : public Base<int>, public Base<float> {
public:
  // NOTE: no my_callback() implementation here
};

class Derived2 : public Base<int>, public Base<float> {
public:
  virtual void my_callback() {
    cout << "Derived2::my_callback()\n";
  }
};


int main()
{
  {
    Derived1 d;
    Base<int> * i_p = &d;
    Base<float> * i_f = &d;
    i_p->my_callback();
    i_f->my_callback();
  }
  {
    Derived2 d;
    Base<int> * i_p = &d;
    Base<float> * i_f = &d;
    i_p->my_callback();
    i_f->my_callback();
  }
}

Вывод:

Base<int>::my_callback()
Base<float>::my_callback()
Derived2::my_callback()
Derived2::my_callback()

Попробую объяснить почему:

Derived1 d;
Base<int> * i_p = &d;
Base<float> * i_f = &d;

// will check the vtable, and will call
//  either Derived1::my_callback
//  OR Base<int>::my_callback
i_p->my_callback();

// will check the vtable, and will call
//  either Derived1::my_callback
//  OR Base<float>::my_callback
i_f->my_callback();

Хотя через vtable есть две версии my_callback() в классе Derived1, вы НЕ МОЖЕТЕ переопределить ни одну из них, вы можете переопределить только обе сразу (как это делает Derived2 в примере)!

Вы должны просто предоставить два метода "my_callback1()" и "my_callback2()".

person Frunsi    schedule 13.12.2009

Независимо от того, используете ли вы класс шаблона или не шаблон, это можно сделать с помощью вспомогательных классов в этом стиле или этот.

(Кажется, это единственное переносимое решение, если вы не планируете использовать специальные полные имена Microsoft.)

person Masood Khaari    schedule 06.03.2013

Разве виртуальность, связанная с template, не вызывает у вас тревогу? ;)

Вы должны выбрать свою сторону, либо статическую, либо динамическую.

person NewbiZ    schedule 13.12.2009
comment
Разве виртуальность, связанная с шаблоном, не вызывает в вашей голове тревогу? С чего бы? - person curiousguy; 01.11.2011