15

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

例如:

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

4 回答 4

27

C++ 中的类可以有多个基类,因此具有“get me the base”特性是没有意义的。

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

我不确定这是否会进入 C++14,或者是否会独立发布,但 GCC似乎已经支持这个

于 2013-04-28T11:25:43.347 回答
8

这可能是一个不错的方法,具体取决于您的用例。声明以base基类本身命名的基类的 typedef。

然后派生类X将继承它作为 typename X::base

如此,如此。B::base_AC::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>();
}
于 2013-04-28T16:54:58.070 回答
8

我想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
于 2013-04-28T11:54:04.527 回答
1

有一定的限制,这是可能的!

  • 需要以这种方式检测的每个碱基都必须继承自某个 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,
        std::enable_if_t<std::is_base_of_v<T, D>, std::nullptr_t> = nullptr,
        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 调用此重载函数,并且由于考虑并尝试实例化来自所有基的重载,因此您将获得一个基列表。
于 2021-06-01T17:34:07.880 回答