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();
}
Живой пример .