如何测试类型是否具有非类型参数的模板专门化?

时间:2021-11-11 17:04:09

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_2 ::value。但是,这并不实用,因为如果您添加另一个模板参数,您必须编辑您的特性。此外,如果您有这种类型的几个模板,您需要为每个模板编写一个特性。 ,>

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.

有没有什么方法可以让事情变得更实际?我可以使用c++ 14。我并不是说使用宏来减少代码量。

2 个解决方案

#1


8  

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:

如果您可以修改F,您可以通过将常量封装到瘦类型中来使其更加一致。所以:

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.

如果您对F的控制更少,您可以使用您的方法,或者编写元程序,将模板和该模板的实例与类型包装器结合起来。

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.

现在,type_lift_instance可以专门用于多种类型,可以使用一些decltype magic来为不同类型提取type_lifted_template专门化。

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).

这并不是更少的输入,但是元化与is代码(或其他类似的代码)相去甚远。

I'm probably using "lift" incorrectly.

我可能用错了“lift”。

#2


4  

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:

如果你可以修改F并且没有其他你没有提到的限制,最简单的解决方案就是添加一个唯一的基类:

#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" );
}

#1


8  

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:

如果您可以修改F,您可以通过将常量封装到瘦类型中来使其更加一致。所以:

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.

如果您对F的控制更少,您可以使用您的方法,或者编写元程序,将模板和该模板的实例与类型包装器结合起来。

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.

现在,type_lift_instance可以专门用于多种类型,可以使用一些decltype magic来为不同类型提取type_lifted_template专门化。

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).

这并不是更少的输入,但是元化与is代码(或其他类似的代码)相去甚远。

I'm probably using "lift" incorrectly.

我可能用错了“lift”。

#2


4  

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:

如果你可以修改F并且没有其他你没有提到的限制,最简单的解决方案就是添加一个唯一的基类:

#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" );
}