Rufen Sie die Basisklasse für einen Typ in der Klassenhierarchie ab

Lesezeit: 6 Minuten

Ist es möglich, den Basisklassentyp in einer Klassenhierarchie zu erhalten?

Beispielsweise:

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

Ich möchte eine Vorlage, die haben wird typedef Base<T>::Type innen so:

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

Ist das möglich? Was ist, wenn ich Mehrfachvererbung habe?

Rufen Sie die Basisklasse fur einen Typ in der Klassenhierarchie
Kerrek SB

Klassen in C++ können mehr als eine Basisklasse haben, daher macht es keinen Sinn, eine “get me das Grundeigenschaft.

Die TR2-Ergänzungen enthalten jedoch neue Compiler-unterstützte Merkmale std::tr2::bases und std::tr2::direct_bases, die eine undurchsichtige Typenliste von Basisklassen zurückgibt.

Ich bin mir nicht sicher, ob dies es in C++ 14 schaffen wird oder ob es unabhängig veröffentlicht wird, aber GCC bereits scheint dies zu unterstützen.

  • Das ist eine Art Typ-Eigenschaft, die ohne Compiler-Magie nicht implementiert werden kann, oder?

    – jrok

    28. April 13 um 12:13 Uhr

  • Dies ist für meine Frage besser geeignet, aber ich habe die andere Antwort akzeptiert, da sie in Kombination mit std::conditional mein Problem löst.

    – Mircea Ispas

    28. April 13 um 12:21 Uhr


  • @jrok: Nun, offensichtlich nicht, wie würde die Eigenschaft etwas über die Typen im Programm erfahren? Auf der anderen Seite sind dies die Art von Kleinigkeiten, die uns einem vollständigen Reflektionssystem zur Kompilierzeit näher bringen.

    – Matthias M.

    28. April 13 um 12:40 Uhr

  • @Felics wie kann das nicht die akzeptierte Antwort sein? Ihre Frage war nicht, ob B ist in der Tat eine Basis von D (Zuordnung zu bool), aber welche B ist D abgeleitet von (one-to-many-Mapping).

    – TemplateRex

    28. April 13 um 19:43 Uhr


  • @Felics nebenbei, in Zukunft beschreiben beide Ihre Real Problem und das Problem, das Sie mit Ihrem Lösungsversuch haben. Stack Overflow eignet sich zwar gut zum Gedankenlesen, funktioniert aber noch besser, wenn es nicht sein muss!

    – Yakk – Adam Nevraumont

    28. April 13 um 20:44 Uhr

1643039405 458 Rufen Sie die Basisklasse fur einen Typ in der Klassenhierarchie
masud

Ich denke std::is_base_of kann dir helfen

#include <type_traits>

std::is_base_of<B, D>()

Wenn D von B abgeleitet ist oder wenn beide dieselbe Nicht-Union-Klasse sind, wird der Elementkonstantenwert gleich true bereitgestellt. Andernfalls ist der Wert falsch.

Sie können es verwenden, um zu überprüfen, ob eine Klasse die Basisklasse einer anderen ist oder nicht:

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

1643039405 922 Rufen Sie die Basisklasse fur einen Typ in der Klassenhierarchie
Andreas Tomassos

Dies kann je nach Anwendungsfall eine gute Möglichkeit sein. Deklarieren Sie eine Typedef der benannten Basisklasse base in der Basisklasse selbst.

Dann abgeleitete Klassen X erbt es als Typnamen X::base.

So B::base ist A, und C::base ist A.

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>();
}

1643039406 415 Rufen Sie die Basisklasse fur einen Typ in der Klassenhierarchie
Heilige Schwarze Katze

Mit gewissen Einschränkungen ist es möglich!

  • Jede Base, die auf diese Weise nachweisbar sein muss, muss von einer bestimmten CRTP-Base erben. (Oder möglicherweise eine Art Makro enthalten.)

  • Sie erhalten eine Liste aller Eltern, auch der indirekten.

Auf gcc.godbolt.org ausführen

#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
}

Hier ist, was los ist:

  • Sie verwenden die zustandsbehaftete Vorlagenmetaprogrammierung, um eine Liste von Typen zu erstellen, an die Sie Typen anhängen können, indem Sie eine bestimmte Vorlage instanziieren.
  • Unter Verwendung einer CRTP-Basis fügen Sie a hinzu friend Funktion für jede Klasse, die auf diese Weise erkannt werden muss. Diese Funktionen werden mit SFINAE unaufrufbar gemacht, aber lediglich in Anbetracht sie während der Überladungsauflösung instanziiert die Vorlage, die die entsprechende Basisklasse an die Liste anfügt.
  • Sie rufen diese überladene Funktion mit ADL auf, und da Überladungen von allen Basen berücksichtigt und versucht werden, instanziiert zu werden, erhalten Sie eine Liste von Basen.

.

620040cookie-checkRufen Sie die Basisklasse für einen Typ in der Klassenhierarchie ab

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy