ищу решение переопределения функции-члена c ++ (не виртуально) - PullRequest
1 голос
/ 22 июля 2011

У меня есть два класса:

struct A {

    template <typename T>
    void print(T& t){ 
        // do sth specific for A
    }
};

struct B : A {

    template <typename T>
    void print(T& t){ 
        // do sth specific for B
    }
};

В таком случае более общий базовый класс с виртуальными функциями (от которых наследуются A и B) не может быть скомпилирован, так как для шаблона нет виртуального. Когда я пытаюсь делегировать все объекты A или B под одним и тем же «интерфейсом», у кого-нибудь есть идея решить эту проблему? Заранее спасибо.

С уважением, Июнь

Ответы [ 5 ]

3 голосов
/ 22 июля 2011

Вы можете подумать об использовании CRTP .

template<typename Derived>
struct Base {
  template <typename T>
  void print(T& t){ 
    static_cast<Derived*>(this)->print(t);
  }
};

struct A : Base<A> {
// template print
};

struct B : Base<B> {
// template print
};

Пример использования:

template<typename T, typename ARG>
void foo (Base<T>* p, ARG &a)
{
  p->print(a);
}

Этот метод будет называться,

foo(pA, i); // pA is A*, i is int
foo(pB, d); // pB is B*, d is double

Вот еще один демонстрационный код .

2 голосов
/ 19 октября 2012

Использование прокси-класса для получения метода B

  class A {
    public:
        friend class CProxyB;
        virtual CProxyB* GetCProxyB() = 0;
    };

    class B;
    class CProxyB
    {
    public:
        CProxyB(B* b){mb = b;} 
        template <typename T>
        void printB(T& t)
        { 
            mb->print(t);
        }       

        B* mb;
    };

    class B:public A {
    public:
        virtual CProxyB* GetCProxyB(){return new CProxyB(this);};

        template <typename T>
        void print(T& t){ 
            printf("OK!!!!!\n");
        }
    };


    int _tmain(int argc, _TCHAR* argv[])
    {
        A* a = new B;

        CProxyB* pb = a->GetCProxyB();

        int t = 0;
        pb->printB(t);

        return 0;
    }
0 голосов
/ 22 июля 2011

Используя CRTP (Любопытно повторяющийся шаблон) , вы можете добиться статического полиморфизма без виртуального.

#include <iostream>
using namespace std;
#define MSG(msg) cout << msg << endl;

template<class Derived>
class Base{
public:
    void print()
    {
        static_cast<Derived*>(this)->print();
    }
};

class Derived1 : public Base<Derived1>
{
public:
    void print()
    {
        MSG("Derived 1::print");
    }


};

class Derived2 : public Base<Derived2>
{
public:
    void print()
    {
        MSG("Derived 2::print");
    }
};

template<class T>
void callme(Base<T>& p)
{
     p.print();
}

int main() 
{
    Base<Derived1> p1;
    Base<Derived2> p2;
    callme(p1);
    callme(p2);
    system("pause");
    return 0;
}

//Result :
//Derived 1::print
//Derived 2::print
0 голосов
/ 22 июля 2011

У меня вопрос, как использовать один указатель на разные объекты A или B.

Вы можете сделать это без виртуальных функций как таковых. Но все, что вы действительно будете делать, это написать реализацию V-таблицы и виртуальных функций.

Если бы я собирался вручную реализовать виртуальные функции, я бы основывал все это на объекте Boost.Variant. Вариант будет эффективно хранить данные члена для каждого класса. Чтобы вызвать функцию, вы используете вариант функтора посетителя. Каждая «виртуальная функция» будет иметь свой собственный функтор посетителей, который будет иметь разные перегрузки оператора () для каждого из возможных типов в варианте.

Так что вы можете иметь это:

typedef boost::variant<StructA, StructB, StructC> VirtualClass;

Вы можете сохранить любой из этих объектов в варианте. Вы бы вызвали «виртуальную функцию» на объекте так:

VirtualClass someObject(StructA());
boost::apply_visitor(FunctorA(), someObject);

Класс FunctorA - это ваша виртуальная реализация функции. Это посетитель, определяемый так:

class FunctorA : public boost::static_visitor<>
{
  void operator()(StructA &arg){
    //Do something for StructA
  }

  void operator()(StructB &arg){
    //Do something for StructB
  }

  void operator()(StructC &arg){
    //Do something for StructC
  }
}

Посетители могут иметь возвращаемые значения, которые возвращаются apply_visitor. Они могут принимать аргументы, сохраняя аргументы как члены класса посетителя. И так далее.

Лучше всего, если вы когда-нибудь измените свой тип варианта, добавив новые «производные классы», вы получите ошибки компилятора для любых функторов, у которых нет перегрузок для новых типов.

Но, честно говоря, вы должны просто использовать виртуальные функции.

0 голосов
/ 22 июля 2011

Два варианта:

Вариант один: виртуализировать метод, где, если пользователь не предоставляет реализацию, используется базовый класс '.

 
template <typename T>
struct A {
    virtual void print(T& t);
};

template <typename T>
void A::print(T& t) {
    // do sth specific for A
}

template <typename T>
struct B : A {
    virtual void print(T& t);
};

void B::print(T& t) {
    // do sth specific for B
}

 

Вариант два: Абстрактный методгде, если пользователь не предоставляет реализацию, код не будет компилироваться.

 
template <typename T>
struct A {
    virtual void print(T& t)=0;
};

template <typename T>
struct B : A {

    virtual void print(T& t){ 
        // do sth specific for B
    }
};
template <typename T>
void B::print(T& t){ 
    // do sth specific for B
}
 

Если не указано выше, если вы не сделаете их виртуальными, класс Derived будет использовать метод класса Shadow Base иэто, безусловно, не то, что вы хотели.Следовательно, невозможно.

...