假设我上了动作
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;
有这种可能吗?
答案 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();
}