简体   繁体   中英

How to test if type is specialization of template with non-type parameters?

I was wondering if there was any solution to find if a type was a specialization of a template that takes non-type parameters without specifying every type ?

For instance, if have a class like this:

template<typename T, std::size_t R>
struct F {}

For now, I'm using a very specialized traits:

template<template<typename, std::size_t> class TT, typename T>
struct is_2 : std::false_type { };

template<template<typename, std::size_t> class TT, typename V1, std::size_t R>
struct is_2<TT, TT<V1, R>> : std::true_type { };

and used like is_2<F, T>::value . However, this is not practical since, if you add another template parameter, you have to edit your traits. Moreover, if you have several templates of this kind, you need to write a traits for each of them.

Is there any way to make something more practical ? I can use C++14. And I don't mean using a macro to reduce the code amount.

Non-type template parameters are a bit of a red headed stepchild.

There is no "any template parameter is matched, type or not".

If you can modify F , you make it more uniform by wrapping your constants in thin types. So:

template<typename T, class R>
struct F;

template<typename T, std::size_t R>
struct F<T, std::integral_constant<std::size_t, R>> {};

now meta-programs like is can be written uniformly:

template<template<class...>class Template, class T>
struct is_instantiation : std::false_type {};
template<template<class...>class Template, class... Ts>
struct is_instantiation<Template, Template<Ts...>> : std::true_type {};

matching everything.

If you have less control over F , you can either use your approach, or write metaprogram that hoists both a template and an instance of that template into something with type wrappers.

struct meta_F {
  template<class T, std::size_t R>using raw_apply=F<T,R>;
  template<class T, class R>using apply=raw_apply<T,R::value_type>;
};

template<class meta_Template, class... Args>
struct type_lifted_template {};

template<class T, std::size_t R>
struct type_lifted_template< meta_F, T, std::integral_constant<std::size_t, R> > {
  using result = meta_F::template raw_apply<T, R>;
};

template<class T, std::size_t R>
auto type_lift_instance( F<T,R> )
-> type_lifted_template< meta_F, T, std::integral_constant<std::size_t, R> >;

Now, type_lift_instance can be specialized for multiple types, and some decltype magic could be used to extract the type_lifted_template specialization for different types.

All of this is pretty rough. You'd be best, if you are doing lots of meta programming on templates, to just have your templates take uniform type parameters, instead of messing around with this stuff.

template<class meta_F, class C>
struct meta_template_is_lifted : std::false_type {};
template<class meta_F, class...Ts>
struct meta_template_is_lifted<meta_F, type_lifted_template< meta_F, Ts... >> : std::true_type {};

template<class meta_F, class C>
struct meta_template_is : meta_template_is_lifted< meta_F, decltype(type_lift_instance( std::declval<C>() ) ) > {};

this isn't much less typing, but the metafication goes on far away from the is code (or other similar code).

I'm probably using "lift" incorrectly.

If you can modify F and there are no other restrictions you haven't mentioned, the easiest solution would be to add a unique base class:

#include <cstddef>
#include <type_traits>

struct unique_F_base {};

template<typename T, std::size_t R>
struct F : unique_F_base
{
};

template<typename T>
using is_F = std::is_base_of<unique_F_base,T>;

int main()
{
    static_assert( !is_F< int >::value, "Oops" );
    static_assert( is_F< F<int,42> >::value, "Oops" );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM