Тип шаблона класса в наследовании - PullRequest
0 голосов
/ 03 декабря 2018

Допустим, у меня есть класс Действие

template<class T>
class Action {
public:
    virtual ~Action() = default;

    virtual void execute(T &object) const = 0;
};

, которое может быть выполнено для некоторого объекта типа T

Далее у меня естькласс Object

class Object {
public:
    Object() : actions() {}

    virtual ~Object() = default;

    virtual const std::string getName() const = 0;

    void addAction(const Action<Object> *action) {
        actions.push_back(action);
    }

    void execute() {
        for (auto &action : actions) {
            action->execute(*this);
        }
    }

private:
    std::vector<const Action<Object> *> actions;
};

, который содержит вектор действий, которые могут быть выполнены сразу.

Теперь у меня есть конкретный ObjectA

class ObjectA : public Object {
public:
    const std::string getName() const override {
        return "ObjectA";
    }
};

и два конкретных действия ActionA , ActionB

class ActionA : public Action<ObjectA> {
    void execute(ObjectA &object) const override {
        std::cout << "ActionA on " << object.getName() << std::endl;
    }
};

class ActionB : public Action<ObjectA> {
    void execute(ObjectA &object) const override {
        std::cout << "ActionB on " << object.getName() << std::endl;
    }
};

Использование в том, что я создаю ObjectA, добавляю к нему оба действияи выполните их.

int main() {
    ObjectA object = ObjectA{};

    object.addAction(reinterpret_cast<const Action<Object> *>(new ActionA()));
    object.addAction(reinterpret_cast<const Action<Object> *>(new ActionB()));

    // This is what I want to achieve instead of using reinterpret_cast
    //object.addAction(new ActionA());
    //object.addAction(new ActionB());

    object.execute();
}

Вывод должен быть

ActionA on ObjectA
ActionB on ObjectA

Проблема в том, что для его компиляции я должен использовать reinterpret_cast.Проблема, вероятно, заключается в определении std::vector<const Action<Object> *> actions; Я хотел бы создать шаблон, поэтому в ObjectA это похоже на std::vector<const Action<ObjectA> *> actions;

Возможно ли что-то подобное?

1 Ответ

0 голосов
/ 03 декабря 2018

Action<Object> и Action<ObjectA> являются несвязанными типами в системе типов C ++.

Более того, Action<Object> позволяет вызываться с большим количеством типов , чем Action<ObjectA>.Таким образом, игнорируя систему типов, Action<ObjectA> не может выполнить контракт, который Action<Object> обещает, что он может выполнить.

Однако Action<Object> может выполнить обещание, что Action<ObjectA>make.

Известны два типа отношений типа OO;ковариация и контравариантность.Action<T> является котравариантным в T, Action<Base> может использоваться для выполнения контракта Action<Derived>.


Итак, подход.

Во-первых, ваш Action<T>является плохо написанной указательно-семантической версией std::function<void(T&)>.Используйте это вместо этого.

Теперь у вас есть семантика значения.

template<class T>
using Action=std::function<void(T&)>;

class Object {
public:
    Object() = default;

    virtual ~Object() = default;

    virtual const std::string getName() const = 0;

    void addAction(Action<Object> action) {
        actions.emplace_back(std::move(action));
    }

    void execute() {
        for (auto &action : actions) {
            action(*this);
        }
    }

private:
    std::vector<Action<Object>> actions;
};

ах, намного приятнее.

Это, однако, не решит вашу проблему.

auto ActionA = Action<ObjectA>{
  [](ObjectA &object) {
    std::cout << "ActionA on " << object.getName() << std::endl;
  }
};

ActionA нельзя присвоить Action<Object>, поскольку Action<Object> может быть передано не-ObjectA, и он должен сделать что-то с ним.

Ваш оригинальный код override не будет компилироваться.

Мы должны решить, хотим ли мы притвориться Action<Object>, что мы должны делать, если типы не соответствуют?Вот один из вариантов:

template<class T, class F>
auto only_when_dynamic_type_matches( F&& f ) {
  if constexpr( std::is_pointer< T >{} ) {
    return
      [f=std::forward<F>(f)](auto* x)->void{
        auto* t = dynamic_cast<T>(x);
        if (!t) return
        f(t);
      };
  } else {
    return
      [f=std::forward<F>(f)](auto&& x)->void{
        auto* t = dynamic_cast<std::remove_reference_t<T>*>(std::addressof(x));
        if (!t) return;
        f(*t);
      };
  }
}

теперь мы можем написать

auto ActionA = only_when_dynamic_type_matches<ObjectA&>([](auto&&object) {
  std::cout << "ActionA on " << object.getName() << std::endl;
});
auto ActionB = only_when_dynamic_type_matches<ObjectA&>([](auto&&object) {
  std::cout << "ActionB on " << object.getName() << std::endl;
});

затем

int main() {
    ObjectA object = ObjectA{};

    object.addAction(ActionA);
    object.addAction(ActionB);

    object.execute();
}

Живой пример .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...