Получить самый глубокий класс в цепочке наследования CRTP - PullRequest
3 голосов
/ 10 июня 2019

Я хотел бы знать, как решить следующую проблему (C ++ 17): предположим, есть несколько шаблонных классов, унаследованных друг от друга в стиле CRTP (только одиночное наследование).Для заданного базового класса экземпляра шаблона найдите класс, который находится дальше всего от него по цепочке наследования.

Сначала я подумал, что это должно быть довольно легко, но не смог этого сделать.

Для упрощения предположим, что каждый корень и каждый промежуточный класс имеют using DerivedT = Derived в своей области public.

Пример:

template <class T>
struct GetDeepest {
    using Type = ...;
};

template <class T>
struct A {
    using DerivedT = T;
};

template <class T>
struct B : public A<B<T>> {
    using DerivedT = T;
};

struct C : B<C> {
};

struct D : A<D> {
};

GetDeepest<A<D>>::Type == D;
GetDeepest<B<C>>::Type == C;
GetDeepest<A<B<C>>>::Type == C;
...

Первая реализация, которую я пробовал:

template <class T>
struct GetDeepest {
    template <class Test, class = typename Test::DerivedT>
    static std::true_type Helper(const Test&);
    static std::false_type Helper(...);

    using HelperType = decltype(Helper(std::declval<T>()));
    using Type = std::conditional_t<std::is_same_v<std::true_type, HelperType>,
                    GetDeepest<typename T::DerivedT>::Type,
                    T>;
};

Вторая реализация, которую я пробовал:

template <class T>
struct HasNext {
    template <class Test, class = typename Test::DerivedT>
    static std::true_type Helper(const Test&);
    static std::false_type Helper(...);

    using HelperType = decltype(Helper(std::declval<T>()));
    static const bool value = std::is_same_v<std::true_type, HelperType>;
};

template <class T>
auto GetDeepestHelper(const T& val) {
    if constexpr(HasNext<T>::value) {
        return GetDeepestHelper(std::declval<typename T::DerivedT>());
    } else {
        return val;
    }
}

template <class T>
struct GetDeepest {
    using Type = decltype(GetDeepestLevelHelper(std::declval<T>()));
};

Ни одна из них не компилируется.

Первая - из-за неполного типа GetDeepest<T> в выражении using Type = ...во-вторых, из-за рекурсивного вызова функции с auto в качестве возвращаемого типа.

Можно ли вообще реализовать класс GetDeepest<T> с такими свойствами?Теперь мне очень любопытно, даже если это не самый лучший способ достичь того, чего я хочу.

Спасибо!

1 Ответ

4 голосов
/ 11 июня 2019

Я не уверен, что полностью понимаю вопрос, поэтому не стесняйтесь спрашивать меня в комментариях.

Но я думаю, что это должно работать:

#include <type_traits>

template<typename T>
struct GetDeepest
{
    using Type = T;
};

template<template<typename> class DT, typename T>
struct GetDeepest<DT<T>>
{
    using Type = typename GetDeepest<T>::Type;
};

template <class T>
struct A {
    using DerivedT = T;
};

template <class T>
struct B : public A<B<T>> {
    using DerivedT = T;
};

struct C : B<C> {
};

struct D : A<D> {
};

int main()
{
    static_assert(std::is_same<GetDeepest<A<D>>::Type, D>::value);
    static_assert(std::is_same<GetDeepest<B<C>>::Type, C>::value);
    static_assert(std::is_same<GetDeepest<A<B<C>>>::Type, C>::value);

}
...