#include <iostream>
#include <typeinfo>
#include <type_traits>

struct Grandma             {};
struct Mom:        Grandma {};
struct Daughter:   Mom     {};
struct Son:        Mom     {};
struct Grandchild: Son     {};

struct Stranger            {};

namespace detail
{

    struct TypeIsNotPartOfTheHierarchy {};

    template<typename T>
    struct TypeWrapper
    {
        static_assert(!std::is_same<TypeIsNotPartOfTheHierarchy, T>::value,
            "using types of different type hierarchies.");

        using type = T;
    };

    template<typename StillCommonAncestor, typename TypeToCheck, typename... Ts>
    struct IsCommonAncestor;

    template<typename StillCommonAncestor, typename TypeToCheck>
    struct IsCommonAncestor<StillCommonAncestor, TypeToCheck>
    {
        static constexpr bool value = StillCommonAncestor::value;
    };

    template<typename StillCommonAncestor, typename TypeToCheck, typename T1, typename... Ts>
    struct IsCommonAncestor<StillCommonAncestor, TypeToCheck, T1, Ts...>:
        IsCommonAncestor
        <
            std::integral_constant
            <
                bool,
                std::conditional
                <
                    std::is_base_of<TypeToCheck, T1>::value,
                    std::true_type,
                    std::false_type
                >::type::value && StillCommonAncestor::value
            >,
            TypeToCheck,
            Ts...
        >
    {};

    template<typename Pack, typename... Ts>
    struct LCA;

    template<typename... PackParams, typename T1>
    struct LCA<std::tuple<PackParams...>, T1>:
        std::conditional
        <
            IsCommonAncestor<std::true_type, T1, PackParams...>::value,
            TypeWrapper<T1>,
            TypeWrapper<TypeIsNotPartOfTheHierarchy>
        >::type
    {};

    template<typename... PackParams, typename T1, typename... Ts>
    struct LCA<std::tuple<PackParams...>, T1, Ts...>:
        std::conditional
        <
            IsCommonAncestor<std::true_type, T1, PackParams...>::value,
            TypeWrapper<T1>,
            LCA<std::tuple<PackParams...>, Ts...>
        >::type
    {};

}

template<typename... Ts>
struct LCA: detail::LCA<std::tuple<Ts...>, Ts...>
{};

int main()
{
    std::cout << typeid(LCA<Son, Daughter, Mom, Grandchild, Grandma, Son, Son>::type).name() << std::endl;
    std::cout << typeid(LCA<Son>::type).name() << std::endl;
    std::cout << typeid(LCA<Grandma, Daughter, Son>::type).name() << std::endl;

    // error because Daughter and Son are orphans in the list
    // std::cout << typeid(LCA<Daughter, Son>::type).name() << std::endl;

    // error because the Son and his Grandma are not related to Stranger.
    // std::cout << typeid(LCA<Grandma, Stranger, Son>::type).name() << std::endl;

    return 0;
}