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

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

Я создал иерархию классов, используя статический полиморфизм через 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


person Kognido    schedule 05.06.2016    source источник
comment
создание виртуальной функции «принять» было бы неуместно, поскольку для сохранения статической части моей программы требуется определенный посетитель. В этом мало смысла без контекста. Можете ли вы показать, что вы имеете в виду, демонстрируя конкретную проблему или ошибку? В общем, как только вы поместили указатели на производные объекты в контейнер базовых указателей, вы стерли типы, и единственный способ вернуть их — это вызвать виртуальную функцию (динамическое приведение не учитывается из-за проблем с масштабируемостью).   -  person n. 1.8e9-where's-my-share m.    schedule 05.06.2016
comment
Я немного перефразировал вопрос, чтобы прояснить. У меня есть набор статических посетителей, которых я хочу передать функции «принять», чтобы посетитель выполнял некоторые вычисления в объекте. Это может быть достигнуто путем создания шаблона класса и вызова функции принятия производного класса. Однако я также хочу перебрать набор объектов данных. Чтобы сделать это, сохраняя иерархию статического полиморфизма, я реализовал класс vbd(virtualbaseddata). Таким образом, я не могу сделать класс vbd классом шаблона, поскольку я не могу перебирать классы шаблонов.   -  person Kognido    schedule 05.06.2016
comment
Когда типы не известны статически, как в случае с контейнером указателей базового класса, мало шансов на статический полиморфизм. Старый добрый полиморфизм времени выполнения, также известный как ООП, подходит. Это не исключает статического полиморфизма в других частях программы.   -  person n. 1.8e9-where's-my-share m.    schedule 05.06.2016


Ответы (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) и вызывать нужный метод с нужными аргументами для каждого из них.

person pzelasko    schedule 05.06.2016

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

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

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

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

Как вы сказали, простого решения нет, но вы можете использовать полиморфизм времени выполнения вместо статического. Чтобы заархивировать полиморфизм времени выполнения, вам необходимо выполнить стирание типов в базовом классе и отправить его в соответствующие функции, не связанные с шаблоном. Это можно заархивировать, например, с помощью 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;
}
person paweldac    schedule 05.06.2016
comment
Хм, я попробую эту реализацию, однако это как-то помешало бы цели, если бы мне пришлось выполнять полиморфизм во время выполнения в этой конкретной части программы. - person Kognido; 05.06.2016
comment
Я не думаю, что это выполнимо (по крайней мере, я не знаю никакого возможного решения) со статическим полиморфизмом. - person paweldac; 05.06.2016
comment
С этой реализацией, как я могу вызвать правильную функцию посещения? Учтите, что у меня есть более 3 классов посетителей, которые можно передать функции принятия. Внутри функции принятия я хочу вызвать функцию посещения посетителей, однако я не знаю, с каким посетителем я сейчас работаю. Чтобы еще больше усложнить ситуацию, поскольку они являются статическими посетителями, они не имеют общего родителя. - person Kognido; 05.06.2016
comment
если они являются статическими посетителями, просто передайте std::function в качестве аргумента, и шаблон не требуется (если я правильно понимаю, что вы подразумеваете под статическим посетителем). Чтобы различать этих посетителей, я бы создал класс BaseVisitor, который обертывает boost::any как член и другой член, с помощью которого вы сможете различать этих посетителей, например строку или перечисление (это будет менее гибко). При приеме посетителя получите идентификатор его типа и обработайте каждого посетителя должным образом. Чтобы избежать нескольких операторов if-else, используйте карту с идентификатором посетителя и привязанным обработчиком. - person paweldac; 05.06.2016