Мультиметоды C++ и определение времени компиляции

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

class A{};
class B: public A{};
class C: public A{};

class MyVisitor
{
   public:
       void visit(B*);
       void visit(C*);
};

И затем коллекция объектов A *, я хочу добиться следующего:

1)

MyVisitor visitor;
for(vector<A*>::iterator it = vec.begin(); it!= vec.end();it++)
     visitor->visit(a);

2) Как-то определить во время компиляции, указывает ли A * на производный объект D, и дать ошибку компилятора, если функция MyVisitor::visit(D*) отсутствует

Я знаю, что 1) достижимо с некоторой реализацией мультиметодов, я думаю, я могу найти некоторые реализации мультиметодов для С++. Но возможно ли 2) как-то?


person user152508    schedule 29.08.2014    source источник
comment
Я предполагаю, что под мультиметодом вы имеете в виду полиморфное приведение? Вы родом из Clojure?   -  person Sir Digby Chicken Caesar    schedule 29.08.2014
comment
Мультиметоды @SirDigbyChickenCaesar - это понятие, не зависящее от языка (также известное как множественная отправка)   -  person sehe    schedule 29.08.2014
comment
Несмотря на это, нет способа определить (по крайней мере, в системе типов С++), является ли какой-либо произвольный INSTANCE класса производным или базовым классом во время COMPILE после создания экземпляра.   -  person Sir Digby Chicken Caesar    schedule 29.08.2014
comment
В нем отсутствует часть accept в иерархии для Visitor_pattern.   -  person Jarod42    schedule 29.08.2014
comment
Возможно, вас заинтересуют методы, основанные на Boost Variant (с бинарным посещением): ="создание интерфейса без виртуальных функций">stackoverflow.com/questions/18859699/ (я не могу найти другого примера, который, возможно, лучше подходит прямо сейчас)   -  person sehe    schedule 29.08.2014
comment
Возможно, вы захотите взглянуть на некоторые разработанные мной проекты: yorel.be/mm, github.com/jll63/yomm11, codeproject.com/Articles/635264/ Это реализация нескольких методов для C++, которая очень напоминает предложение Stroustrup & al. Это также быстро, это превосходит двойную отправку.   -  person yorel    schedule 29.08.2014
comment
@sehe большое спасибо за вашу ссылку. Это действительно помогло.   -  person user152508    schedule 03.11.2014


Ответы (3)


Вы можете использовать dynamic_cast таким образом (внутри тела вашего цикла for), поскольку поведение должно меняться во время выполнения (в соответствии с фактическим типом данных).

   ClassB* ba = dynamic_cast<ClassB*>(a);
   if (ba)
      visitor->visit(ba);
   ClassC* ca = dynamic_cast<ClassC*>(a);
   if (ca)
      visitor->visit(ca);

Возможно, ваши visit функции могут быть объявлены virtual (для вашей ClassD штуки).

В противном случае организуйте свои классы как дерево (не лес) классов, и ваш самый верхний корневой класс

  class Topmost {
     virtual int classnum() const;

и примите соглашение о том, что каждый неабстрактный класс дает свой уникальный classnum и т. д. Или используйте механизм метакласса (например, Qt есть)

person Basile Starynkevitch    schedule 29.08.2014
comment
Я хочу избежать использования динамического приведения. - person user152508; 29.08.2014
comment
dynamic_cast работает во время выполнения, а не во время компиляции. - person Sir Digby Chicken Caesar; 29.08.2014
comment
Почему вы хотите аннулировать его? Это хорошая причина для его использования. - person Basile Starynkevitch; 29.08.2014

Вы можете попробовать что-то вроде этого.

#include <iostream>

class A
{
    virtual void visit() = 0;
};
class B: private A
{
public:
    void visit()
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
    }
};
class C: private A
{
public:
    void visit()
    {
        std::cout << __PRETTY_FUNCTION__ << "\n";
    }    
};

template <typename... Args>
class MyVisitor : public Args...
{
   public:
       template <typename T>
           void visit(T* t)
       {
           t->visit();
       }
};

int main()
{
    MyVisitor<B, C> visitor;
    B b;
    B* bp = &b;
    visitor.visit(bp);
    return 0;
}

Живой пример

person Community    schedule 29.08.2014
comment
Почему производное от Args...? И вы можете передать любой несвязанный тип, который имеет метод visit, включая тот, который вообще не является производным от A. - person WhozCraig; 29.08.2014

Вы можете полностью применить шаблон посетителя:

class B;
class C;

class IVisitor
{
public:
    void visit(B&) = 0;
    void visit(C&) = 0;
};

class A
{
    virtual ~A() = default;
    virtual void accept(IVisitor& v) = 0;
};

class B: public A{ void accept(IVisitor& v) override { v.visit(*this); } };
class C: public A{ void accept(IVisitor& v) override { v.visit(*this); } };
person Jarod42    schedule 29.08.2014