继承中的类的模板类型

时间:2018-12-03 19:23:08

标签: c++ templates

假设我上了动作

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

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

可以在 T

类型的某些对象上执行

接下来,我有课对象

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 :(得分:0)

Action<Object>Action<ObjectA>是C ++类型系统中不相关的类型。

此外,Action<Object>允许以比Action<ObjectA>更多的类型调用自身。因此,忽略类型系统,Action<ObjectA>无法实现Action<Object>承诺可以履行的合同。

但是,Action<Object> 可以兑现Action<ObjectA>做出的承诺。

有两种著名的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();
}

Live example