Non, common_ancestor_of
nécessiterait une réflexion pour obtenir la classe de base de l'une des classes données.
Si vous avez un ensemble concret de classes, vous pouvez utiliser std::variant
de toutes les classes possibles.
Sinon, vous pouvez utiliser un trait de classe de base (soit un struct base_class
que vous spécialisez pour tous vos types ou un type membre comme using super = ...
o using base = ...
) et trouver manuellement l'ancêtre commun :
template<typename T>
struct type_identity {
using type = T;
};
template<typename... Types>
struct common_ancestor_of;
template<typename T>
struct common_ancestor_of<T> {
using type = T;
};
template<typename T>
struct common_ancestor_of<T, T> {
using type = T;
};
template<typename T, typename U, typename... Rest>
struct common_ancestor_of<T, U, Rest...> : common_ancestor_of<typename common_ancestor_of<T, U>::type, Rest...> {};
template<typename T, typename U>
struct common_ancestor_of<T, U> {
private:
// Base == Derived, so is in it's inheritance chain
template<typename Base, typename Derived, typename std::enable_if<std::is_same<Base, Derived>::value, int>::type = 0>
static constexpr bool in_inheritance_chain(int) {
return true;
}
// Base != Derived, but Derived has a member type `super`, so recursively check `super`
template<typename Base, typename Derived, typename std::enable_if<!std::is_same<Base, Derived>::value && (noexcept(type_identity<typename Derived::super>{}), true), int>::type = 0>
static constexpr bool in_inheritance_chain(int) {
return in_inheritance_chain<Base, typename Derived::super>(0);
}
// Base != Derived and Derived doesn't have a member type `super`, so it isn't in the inheritance chain
template<typename Base, typename Derived>
static constexpr bool in_inheritance_chain(long) {
return false;
}
// T1 is in the inheritance chain for U1, so it is the common ancestor
template<typename T1, typename U1, typename std::enable_if<in_inheritance_chain<T1, U1>(0), int>::type = 0>
static type_identity<T1> find_common_ancestor(int);
// T1 is not in the inheritance chain, so check T1::super
template<typename T1, typename U1>
static decltype(find_common_ancestor<typename T1::super, U1>(0)) find_common_ancestor(long) {}
public:
using type = typename decltype(find_common_ancestor<T, U>(0))::type;
};
template<typename... Types>
using common_ancestor_of_t = typename common_ancestor_of<Types...>::type;
class Animal { };
class Mammal : public Animal { public: using super = Animal; };
class Fish : public Animal { public: using super = Animal; };
class Cat : public Mammal { public: using super = Mammal; };
class Dog : public Mammal { public: using super = Mammal; };
static_assert(std::is_same<typename Cat::super::super, Animal>::value);
static_assert(std::is_same<common_ancestor_of_t<Cat, Dog>, Mammal>::value);
static_assert(std::is_same<common_ancestor_of_t<Cat, Fish>, Animal>::value);
static_assert(std::is_same<common_ancestor_of_t<Fish, Cat>, Animal>::value);
static_assert(std::is_same<common_ancestor_of_t<Cat, Dog, Fish>, Animal>::value);
Cela permet d'obtenir l'ancêtre commun le plus spécialisé, mais pensez à utiliser l'ancêtre commun que vous avez déjà : std::unique_ptr<Animal>
. Si vous écrivez spécifiquement std::unique_ptr<common_ancestor_of_t<Cat, Dog>>
dans votre code, il est tout aussi facile d'écrire std::unique_ptr<Mammal>
. Si c'est derrière un code modèle, std::unique_ptr<Animal>
devrait fonctionner tout aussi bien.