Связь статического посетителя с иерархией статического полиморфизма
Цель моей программы - создать список данных, которые я могу посещать с набором статических посетителей, используя статический полиморфизм в моей иерархии классов.
Я создал иерархию классов, использующих статический полиморфизм через 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;
}