Связь статического посетителя с иерархией статического полиморфизма

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

Я создал иерархию классов, использующих статический полиморфизм через CRTP:

class VirtualBaseData {
public:    
    //someVirtualFunction
}

template<typename Derived>
class BaseData<Derived> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         static_cast<Derived*>(this)->accept(v);
    }
}

class DerivedBaseData1: BaseData<DerivedBaseData> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         //Specific implementation
    }    
}
class DerivedBaseData2: BaseData<DerivedBaseData> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         //Specific implementation
    }    
}

Я хочу сохранить DerivedBaseData в контейнере для последующей итерации и посещения.

int main(){
    std::vector<VirtualBaseData*> dataSet;
    dataSet.push_back(new DerivedBaseData1);
    dataSet.push_back(new DerivedBaseData2);
    for(auto it = fifth.begin(); it != fifth.end(); ++it){
        it->accept(); //Error: VirtualBaseData does not have a member function accept
    }
}

Я ищу способ соединить моих статических посетителей с моей статической иерархией полиморфизма. Мне нужен класс VirtualBaseData в моем статическом полиморфизме, который не является классом шаблона, чтобы использовать классы в контейнерах. Однако, поскольку я не могу иметь класс VirtualBaseData в качестве шаблона, я не могу создать соответствующий static_cast для производного класса, как это сделано в CRTPattern.

У меня вопрос: есть ли у кого-нибудь хорошая стратегия, которая бы сохраняла мои настройки статического полиморфизма, а также статический шаблон посетителей?

Для справки: я реализовал свои статические посетители, как описано на странице 21-23 в http://hillside.net/plop/2006/Papers/Library/portableProgrammingPL.pdf

3 ответа

Если вы не знаете, сколько / какие типы будут у ваших объектов во время компиляции, тогда это вариант использования для динамического полиморфизма (по крайней мере, я не знаю, как это сделать, используя только статический полиморфизм).

Однако... если вы знаете во время компиляции точное количество и типы ваших объектов, теперь мы говорим! Вот минимальный пример компиляции ( код на ideone):

#include <iostream>
#include <tuple>
#include <type_traits>
using namespace std;


template<typename Derived>
class BaseData {
public:
    template<typename Visitor>
    void accept(Visitor &v){
         static_cast<Derived*>(this)->accept(v);
    }
};

class DerivedBaseData1: BaseData<DerivedBaseData1> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
        std::cout << "DerivedBaseData1: accepting visitor " << v << std::endl;
    }    
};
class DerivedBaseData2: BaseData<DerivedBaseData2> {
public:
    template<typename Visitor>
    void accept(Visitor &v){
        std::cout << "DerivedBaseData2: accepting visitor " << v << std::endl;
    }    
};

namespace impl {

    template <size_t N> 
    struct num2type {};

    template <size_t Idx, typename T, typename Visitor>
    void accept_impl(Visitor &v, T &&collection, num2type<Idx>) {
        // run accept on current object
        auto &object = std::get<Idx>(collection);
        object.accept(v);
        // move iteration forward
        accept_impl(v, std::forward<T>(collection), num2type<Idx - 1>{});
    }

    template <typename T, typename Visitor>
    void accept_impl(Visitor &v, T &&collection, num2type<0>) {
        // run accept on current object
        auto &object = std::get<0>(collection);
        object.accept(v);
    }
}

template<typename ...Ts, typename Visitor>
void accept(Visitor &v, std::tuple<Ts...> &&collection) {
    using T = decltype(collection);
    impl::accept_impl(v, std::forward<T>(collection), impl::num2type<std::tuple_size<std::decay_t<T>>::value - 1>{});
}


int main() {
    using visitor_type = int;
    visitor_type visitor = 42;

    DerivedBaseData1 a1, a3;
    DerivedBaseData2 a2;
    accept(visitor, std::tie(a1, a2, a3));

    return 0;
}

Используя статический полиморфизм, вы можете перебирать статическую коллекцию (здесь std::tuple) и вызовите нужный метод с нужными аргументами для каждого из них.

Ошибка верна, потому что вы создаете вектор указателей VirtualBaseData. Класс VirtualBaseData не содержит функцию accept().

Ваш ответ: Может ли шаблон функции-члена класса C++ быть виртуальным?

также читайте о полиморфизме: http://www.cplusplus.com/doc/tutorial/polymorphism/

Как вы сказали, простого решения не существует, но вы можете использовать полиморфизм во время выполнения вместо статического. Чтобы заархивировать полиморфизм во время выполнения, вам нужно выполнить стирание типа в базовом классе и передать его соответствующим функциям, не относящимся к temaplte. Это может быть заархивировано, например, с boost::any из буст библиотеки. Ограничить для boost::any является то, что все объекты хранятся как boost::any должен быть копируемым. Если boost::any не подходит для вас подробнее о стирании типов.

class VirtualBaseData {
public:    
    template <typename T>
    void accept(T& visitor) {
        acceptImpl(boost::any(visitor));
    }
protected:
    virtual void acceptImpl(boost::any const & value ) = 0;
}
Другие вопросы по тегам