[Gelöst] Prüfen, ob eine Klasse eine Instanziierung eines Templates ist



  • Hi zusammen,

    ich möchte über template Metaprogramming und type_traits prüfen, ob eine Klasse eine Instanziierung eines bestimmten templates ist. Leider übersteigt mein Vorhaben meine Kenntnisse, kann mir hier jemand auf die Sprünge helfen?

    Hier mein bisher bester Versuch, basierend auf diesem Ansatz.

    #include <type_traits>
    
    template<std::int64_t num, std::int64_t den>
    struct Base
    {
        static constexpr std::int64_t Num = num;
        static constexpr std::int64_t Den = den;
    };
    
    template<std::int64_t num, std::int64_t den>
    struct Derived1 : Base<num,den>
    {
    };
    
    template<std::int64_t num, std::int64_t den>
    struct Derived2 : Base<num,den>
    {
    };
    
    template<typename T, typename U>
    static constexpr bool is_specialisation_of_v = std::false_type{};
    
    template<template<std::int64_t, std::int64_t> typename T,
             std::int64_t n, std::int64_t d,
             template<std::int64_t, std::int64_t> typename U>
    static constexpr bool is_specialisation_of_v<T<n,d>, U> = std::true_type{};
    
    int main()
    {
        using t1 = Derived1<1,1>;
        using t2 = Derived2<1,1>;
         
        bool const b1 = is_specialisation_of_v<t1,Derived1>;
    }
    

    Hier die dazugehörigen Fehlermeldungen:

    prog.cc:27:54: error: use of template template parameter 'U' requires template arguments
       27 | static constexpr bool is_specialisation_of_v<T<n,d>, U> = std::true_type{};
          |                                                      ^
    prog.cc:26:56: note: template is declared here
       26 |          template<std::int64_t, std::int64_t> typename U>
          |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~          ^
    prog.cc:34:47: error: use of class template 'Derived1' requires template arguments
       34 |     bool const b1 = is_specialisation_of_v<t1,Derived1>;
          |                                               ^
    prog.cc:12:8: note: template is declared here
       11 | template<std::int64_t num, std::int64_t den>
          | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       12 | struct Derived1 : Base<num,den>
          |        ^
    2 errors generated.
    


  • Das Problem ist, dass deine Klassen-Templates auf numerischen Konstanten basieren (nicht auf anderen Klassen/Typen).
    Dieselbe Fehlermeldung gibt es auch (bezogen auf dem Code aus dem Artikel) wenn man std::array verwendet: C++ Compiler Explorer Code

    std::array<int, 10> y;
    std::cerr << is_instance_of_v<decltype(y),std::array> << ' ';  // true
    

    Man muß also dann bei deinem Code immer konkrete Instanziierungen vergleichen, z.B.

    bool const b1 = is_specialisation_of_v<t1,Derived1<1,1>>;
    

    Der Code dazu sähe dann so aus: C++ Compiler Explorer Code

    Nun wird nur Derived1<1,1> als "gleich" angesehen, nicht andere Instanziierungen wie Derived1<1,2>.
    Ich weiß aber nicht, ob du das so haben willst?

    Edit: Das wäre ja einfach typeid(x) == typeid(y) - LOL.
    Hier die abgeänderte Version, so daß jede Instanziierung von Derived1 als gleich angesehen wird: C++ Compiler Explorer Code

    template<template<std::int64_t, std::int64_t> typename T,
             std::int64_t n1, std::int64_t d1,
             std::int64_t n2, std::int64_t d2>
    static constexpr bool is_specialisation_of_v<T<n1,d1>, T<n2,d2>> = std::true_type{};
    


  • @Th69 sagte in Prüfen, ob eine Klasse eine Instanziierung eines Templates ist:

    Man muß also dann bei deinem Code immer konkrete Instanziierungen vergleichen

    Könnte man dann auch std::is_base_of_v verwenden?



  • Auch da funktioniert es nicht mit numerischen Template-Parametern: C++ Compiler Explorer Code

    std::array<int, 10> y;
    std::cerr << std::is_base_of_v<decltype(y),std::array>;
    

    ergibt auch dieselbe Fehlermeldung:

    <source>: In function 'int main()':
    <source>:35:56: error: type/value mismatch at argument 2 in template parameter list for 'template<class _Base, class _Derived> constexpr const bool std::is_base_of_v<_Base, _Derived>'
       35 |   std::cerr << std::is_base_of_v<decltype(y),std::array>
          |                                                        ^
    <source>:35:56: note:   expected a type, got 'array'
    

    Mich würde aber auch interessieren, ob dies generell überhaupt möglich ist (oder ob dies noch ein Defect im Standard ist).



  • Ein Problem ist, dass der Standard nichts darüber aussagt, ob und wann templates als "gleich" angesehen werden. D.h. ein reiner Template Vergleich wird nicht funktionieren.
    Der Trick, den ich nutzen würde, ist eine konkrete Template-Instanz mit einem Template zu vergleichen, indem alle Argumente aus ersterem auch an zweiteres übergeben werden.

    template<typename T,
             template<auto...> typename U>
    constexpr bool is_specialisation_of_v = false;
    
    template<template<auto...> typename T,
             auto... args,
             template<auto...> typename U>
    constexpr bool is_specialisation_of_v<T<args...>, U>  = std::is_base_of_v<U<args...>, T<args...>>; // oder is_same_v, je nachdem, was du erreichen willst
    

    Das Mischen von Template-Argumenten und NTTP ist so leider nicht möglich und müsste für den konkreten Fall behandelt werden.
    https://godbolt.org/z/ecWocY8Kn

    EDIT: Auf ähnliche Art und Weise, habe ich für mimic++ übrigens die "Default-Parameter-Detection" implementiert:
    https://github.com/DNKpp/mimicpp/blob/e4de0a5631ca07062f23cb89bcb429529e529fb3/include/mimic%2B%2B/printing/type/Templated.hpp#L50

        template <typename Type, template <typename...> typename Template, typename LeadingArgList>
        struct is_default_arg_for
            : public std::false_type
        {
        };
    
        template <typename Type, template <typename...> typename Template, typename... LeadingArgs>
            requires requires { typename Template<LeadingArgs...>; }
        struct is_default_arg_for<Type, Template, util::type_list<LeadingArgs...>>
            : public std::bool_constant<
                  std::same_as<
                      Template<LeadingArgs...>,
                      Template<LeadingArgs..., Type>>>
        {
        };
    

    Das erkennt dann, dass z.B. std::allocator<int> das default-argument von std::vector<int, std::allocator<int>> ist.



  • Danke für eure Antworten.

    @DNKpp
    Puh, das muss ich erstmal sacken lassen. Aber es funktioniert, danke!



  • @Th69 sagte in [Gelöst] Prüfen, ob eine Klasse eine Instanziierung eines Templates ist:

    Auch da funktioniert es nicht mit numerischen Template-Parametern: C++ Compiler Explorer Code

    std::array<int, 10> y;
    std::cerr << std::is_base_of_v<decltype(y),std::array>;
    

    ergibt auch dieselbe Fehlermeldung:

    <source>: In function 'int main()':
    <source>:35:56: error: type/value mismatch at argument 2 in template parameter list for 'template<class _Base, class _Derived> constexpr const bool std::is_base_of_v<_Base, _Derived>'
       35 |   std::cerr << std::is_base_of_v<decltype(y),std::array>
          |                                                        ^
    <source>:35:56: note:   expected a type, got 'array'
    

    Mich würde aber auch interessieren, ob dies generell überhaupt möglich ist (oder ob dies noch ein Defect im Standard ist).

    Wobei hier eher das problem ist dass das template std::array keine basis klasse hat. Den typ std::array gibt es nicht. Was auch die Fehlermeldung aussagt:

    <source>:35:56: note:   expected a type, got 'array'
    

    EDIT: Meine Aussage oben ist nicht korrekt. Das liegt nicht daran dass es keine basis klasse gibt. Sondern daran, dass die "check funktion" die "template parameter" aus dem ersten übergebenen typ nicht korrekt an den zweiten übergebenen typ überträgt.
    So wie das die variante von @DNKpp zeigt



  • @DNKpp sagte in [Gelöst] Prüfen, ob eine Klasse eine Instanziierung eines Templates ist:

    Ein Problem ist, dass der Standard nichts darüber aussagt, ob und wann templates als "gleich" angesehen werden. D.h. ein reiner Template Vergleich wird nicht funktionieren.
    Der Trick, den ich nutzen würde, ist eine konkrete Template-Instanz mit einem Template zu vergleichen, indem alle Argumente aus ersterem auch an zweiteres übergeben werden.

    template<typename T,
             template<auto...> typename U>
    constexpr bool is_specialisation_of_v = false;
    
    template<template<auto...> typename T,
             auto... args,
             template<auto...> typename U>
    constexpr bool is_specialisation_of_v<T<args...>, U>  = std::is_base_of_v<U<args...>, T<args...>>; // oder is_same_v, je nachdem, was du erreichen willst
    

    Das Mischen von Template-Argumenten und NTTP ist so leider nicht möglich und müsste für den konkreten Fall behandelt werden.
    https://godbolt.org/z/ecWocY8Kn

    Wobei dein ansatz im folgenden fall nicht funktioniert, wenn die abgeleitete klasse selbst kein template mehr ist:

    struct Derived3 : Base<10,0>
    {
    };
    
    static_assert(is_specialisation_of_v<Derived3, Base>); //<--- error: static assertion failed
    

    Liegt wohl daran, dass das functions template is_specialisation_of_v in diesem falle die template parameter nicht auslesen kann.

    Obwohl ein std::is_base_of_v<Base<10,0>, Derived3> true ergeben würde



  • @firefly Das überrascht mich jetzt nicht, weil das auch gar nicht der Use-Case war. Was genau ist denn nun dein Anliegen?

    @firefly sagte in [Gelöst] Prüfen, ob eine Klasse eine Instanziierung eines Templates ist:

    Wobei hier eher das problem ist dass das template std::array keine basis klasse hat. Den typ std::array gibt es nicht. Was auch die Fehlermeldung aussagt:

    Das geht natürlich nicht, weil std::array kein Typ ist, sondern ein Template. Und wie bereits erwähnt, könnte man auf die Idee kommen, die templates mittels trait zu vergleichen. Allerdings wird das nicht funktionieren, weil der Standard sich darüber ausschweigt, wann zwei Templates als identisch gelten und wann nicht. Ich bin da vor ein paar Jahren schon selbst mal gegen eine Wand gelaufen.



  • @DNKpp sagte in [Gelöst] Prüfen, ob eine Klasse eine Instanziierung eines Templates ist:

    @firefly Das überrascht mich jetzt nicht, weil das auch gar nicht der Use-Case war. Was genau ist denn nun dein Anliegen?

    Naja den satz von DocShoe kann man so interpretieren dass auch abgeleitete Klassen, welche selbst keine templates sind, geprüft werden sollten.

    @DocShoe sagte in [Gelöst] Prüfen, ob eine Klasse eine Instanziierung eines Templates ist:

    "ob eine Klasse eine Instanziierung eines bestimmten templates ist."

    Oder interpretiere ich da zu viel rein.