获取类层次结构中类型的基类

时间:2013-04-28 11:24:05

标签: c++ c++11

是否可以在类层次结构中获取基类类型?

例如:

struct A{};
struct B{} : public A;
struct C{} : public B;

我想要一些内置typedef Base<T>::Type的模板,如下所示:

Base<A>::Type == A
Base<B>::Type == A
Base<C>::Type == A

这可能吗?我有多重继承的情况怎么样?

4 个答案:

答案 0 :(得分:19)

C ++中的类可以有多个基类,所以没有任何意义可以“获得 基础”特征。

但是,TR2的添加包括新的编译器支持的特征std::tr2::basesstd::tr2::direct_bases,它返回基类的不透明类型列表。

我不确定这是否会进入C ++ 14,或者是否会独立发布,但GCC已经seems to support this

答案 1 :(得分:6)

我认为std::is_base_of可以帮助您

#include <type_traits>

std::is_base_of<B, D>()
  

如果D来自B或两者都是相同的非联合类,   提供成员常量值等于true。否则价值就是   假的。

您可以使用它来检查某个类是否是另一个类的基类:

std::is_base_of<A, A>()   // Base<A>::Type == A

std::is_base_of<A, B>()   // Base<B>::Type == A

std::is_base_of<A, C>()   // Base<C>::Type == A

答案 2 :(得分:4)

根据您的使用情况,这可能是一种很好的方法。在基类本身中声明一个名为base的基类的typedef。

然后派生类X会将其作为类型名X::base继承。

因此B::baseAC::baseA

struct A
{
    typedef A base;
};

struct B : A {};
struct C : B {};

template<class X>
void f()
{
    typename X::base x;
}

int main()
{
    f<B>();
    f<C>();
}

答案 3 :(得分:0)

在某些限制下,这是可能的!

  • 需要以这种方式检测的每个碱基都必须从某个 CRTP 碱基继承。 (或者,可能包含某种宏。)

  • 您将获得所有父母的列表,包括间接父母。

Run on gcc.godbolt.org

#include <cstddef>
#include <iostream>
#include <typeindex>
#include <utility>

template <typename T>
struct tag
{
    using type = T;
};

template <typename ...P>
struct type_list
{
    inline static constexpr std::size_t size = sizeof...(P);
};

namespace impl
{
    constexpr void adl_ViewBase() {} // A dummy ADL target.

    template <typename D, std::size_t I>
    struct BaseViewer
    {
        #if defined(__GNUC__) && !defined(__clang__)
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wnon-template-friend"
        #endif
        friend constexpr auto adl_ViewBase(BaseViewer);
        #if defined(__GNUC__) && !defined(__clang__)
        #pragma GCC diagnostic pop
        #endif
    };

    template <typename D, std::size_t I, typename B>
    struct BaseWriter
    {
        friend constexpr auto adl_ViewBase(BaseViewer<D, I>) {return tag<B>{};}
    };

    template <typename D, typename Unique, std::size_t I = 0, typename = void>
    struct NumBases : std::integral_constant<std::size_t, I> {};

    template <typename D, typename Unique, std::size_t I>
    struct NumBases<D, Unique, I, decltype(adl_ViewBase(BaseViewer<D, I>{}), void())> : std::integral_constant<std::size_t, NumBases<D, Unique, I+1, void>::value> {};

    template <typename D, typename B>
    struct BaseInserter : BaseWriter<D, NumBases<D, B>::value, B> {};

    template <typename T>
    constexpr void adl_RegisterBases(void *) {} // A dummy ADL target.

    template <typename T>
    struct RegisterBases : decltype(adl_RegisterBases<T>((T *)nullptr), tag<void>())
    {};

    template <typename T, typename I>
    struct BaseListLow {};

    template <typename T, std::size_t ...I>
    struct BaseListLow<T, std::index_sequence<I...>>
    {
        static constexpr type_list<decltype(adl_ViewBase(BaseViewer<T, I>{}))...> helper() {}
        using type = decltype(helper());
    };

    template <typename T>
    struct BaseList : BaseListLow<T, std::make_index_sequence<(impl::RegisterBases<T>{}, NumBases<T, void>::value)>> {};
}

template <typename T>
using base_list = typename impl::BaseList<T>::type;

template <typename T>
struct Base
{
    template <typename D, typename impl::BaseInserter<D, T>::nonExistent = nullptr>
    friend constexpr void adl_RegisterBases(void *) {}
};


struct A : Base<A> {};
struct B : Base<B>, A {};
struct C : Base<C> {};
struct D : Base<D>, B, C {};

template <typename T>
void printType()
{
    #ifndef _MSC_VER
    std::cout << __PRETTY_FUNCTION__ << '\n';
    #else
    std::cout << __FUNCSIG__ << '\n';
    #endif
};

int main()
{
    static_assert( base_list<D>::size == 4 );
    printType<base_list<D>>(); // typeList<tag<A>, tag<B>, tag<C>, tag<D>>, order may vary
}

这是怎么回事:

  • 您使用有状态模板元编程来创建类型列表,您可以通过实例化特定模板将类型附加到其中。
  • 使用 CRTP 基础,您可以向需要以这种方式检测的每个类添加 friend 函数。 SFINAE 使这些函数无法调用,但仅在重载解析期间考虑它们就会实例化将相应基类附加到列表中的模板。
  • 您使用 ADL 调用此重载函数,并且由于所有基础的重载都被考虑并尝试实例化,因此您将获得基础列表。