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 manstd::array
verwendet: C++ Compiler Explorer Codestd::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 wieDerived1<1,2>
.
Ich weiß aber nicht, ob du das so haben willst?Edit: Das wäre ja einfach
typeof(x) == typeof(y)
- LOL.
Hier die abgeänderte Version, so daß jede Instanziierung vonDerived1
als gleich angesehen wird: C++ Compiler Explorer Codetemplate<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/ecWocY8KnEDIT: 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#L50template <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 vonstd::vector<int, std::allocator<int>>
ist.