可以使用另一个模板和递归来生成一组模板类吗?

huangapple go评论72阅读模式
英文:

is it possible to generate a set of template classes by using another template and recursion?

问题

我想创建一些模板类来断言某种类型的数据:

assertType { static constexpr bool v{false}; };
template<> struct assertType<int> { static constexpr bool v{true}; };
template<> struct assertType<bool> { static constexpr bool v{true}; };
template<> struct assertType<float> { static constexpr bool v{true}; };
template<> struct assertType<long> { static constexpr bool v{true}; };

但是,我想以编程方式实现它,所以我考虑定义一个支持的类型“列表”:

template<typename ...Types>
struct TypesList { };
static inline TypesList<int, bool, float, long> supportedTypes;

然后将这个“列表”传递给另一个模板,通过递归为列表中的每个类型生成“assertType”模板。类似于:

template<typename ...Ts>
struct BuildTemplates { };

template<typename ...Ts>
struct BuildTemplates<TypesList<Ts...>> { };

BuildTemplates<supportedTypes> /* 为int、bool、float和long生成模板 */

这样,我可以使用:

assertType<int>::v // v 应该为 true
assertType<bool>::v // v 应该为 true
assertType<float>::v // v 应该为 true
assertType<long>::v // v 应该为 true

任何其他类型的模板,其类型不属于这些类型之一,应将其v值设置为false。

这是否可行?
提前感谢您。

英文:

I want to create a few template classes in order to assert certain type of data:

assertType { static constexpr bool v{false}; };
template&lt;&gt; struct assertType&lt;int&gt; { static constexpr bool v{true}; };
template&lt;&gt; struct assertType&lt;bool&gt; { static constexpr bool v{true}; };
template&lt;&gt;struct assertType&lt;float&gt; { static constexpr bool v{true}; };
template&lt;&gt;struct assertType&lt;long&gt; { static constexpr bool v{true}; };

However, I would like to do it programmatically, So I thought about defining a "list" of the supported types:

template&lt;typename ...Types&gt;
struct TypesList { };
static inline TypesList&lt;int, bool, float, long&gt; supportedTypes;

And have that "list" passed to another template that, by recursion, generates the "assertType" template for every type in the list. Something like:

template&lt;typename ...Ts&gt;
struct BuildTemplates { };

template&lt;typename ...Ts&gt;
struct BuildTemplates&lt;TypesList&lt;Ts...&gt;&gt; { };

BuildTemplates&lt;supportedTypes&gt; /* Build the templates for int, bool, float and long */

So I can use:

assertType&lt;int&gt;::v // v should be true
assertType&lt;bool&gt;::v // v should be true
assertType&lt;float&gt;::v // v should be true
assertType&lt;long&gt;::v // v should be true

Any other template whose type is not one of those, should have its v value set to false.

Is this possible?
Thanks in advance.

答案1

得分: 6

以下是已翻译的内容:

答案在很大程度上取决于您想要的语法。例如,您可以这样做:

#include <type_traits>

template<typename... Types> struct TypeList {};

using SupportedTypes = TypeList<int, bool, float, long>;

template<typename T>
struct assertType {
    template<class... Types>
    static constexpr bool contains(TypeList<Types...>) {
        return (std::is_same_v<T, Types> || ...);
    }

    static constexpr bool v = contains(SupportedTypes{});
};

static_assert( assertType<int>::v);
static_assert( assertType<bool>::v);
static_assert(!assertType<short>::v);

如果您可以使用boost,可以使用boost::mp11::mp_contains来使实现变得简单:

#include <boost/mp11.hpp>

using SupportedTypes = boost::mp11::mp_list<int, bool, float, long>;

template<typename T>
struct assertType : boost::mp11::mp_contains<SupportedTypes, T> {};

static_assert( assertType<int>::value);
static_assert( assertType<bool>::value);
static_assert(!assertType<short>::value);

或者,如果您想使用v而不是value(这是事实上的标准名称):

template<typename T>
struct assertType {
    static constexpr bool v =
        boost::mp11::mp_contains<SupportedTypes, T>::value;
};
英文:

The answer mostly depends on the syntax you want to get. For example, you could do this:

#include &lt;type_traits&gt;

template&lt;typename... Types&gt; struct TypeList {};

using SupportedTypes = TypeList&lt;int, bool, float, long&gt;;

template&lt;typename T&gt;
struct assertType {
    template&lt;class... Types&gt;
    static constexpr bool contains(TypeList&lt;Types...&gt;) {
        return (std::is_same_v&lt;T, Types&gt; || ...);
    }

    static constexpr bool v = contains(SupportedTypes{});
};

static_assert( assertType&lt;int&gt;::v);
static_assert( assertType&lt;bool&gt;::v);
static_assert(!assertType&lt;short&gt;::v);

If you can use boost, boost::mp11::mp_contains could be employed to make the implementation trivial:

#include &lt;boost/mp11.hpp&gt;

using SupportedTypes = boost::mp11::mp_list&lt;int, bool, float, long&gt;;

template&lt;typename T&gt;
struct assertType : boost::mp11::mp_contains&lt;SupportedTypes, T&gt; {};

static_assert( assertType&lt;int&gt;::value);
static_assert( assertType&lt;bool&gt;::value);
static_assert(!assertType&lt;short&gt;::value);

Or if you want to use v instead of value (which is de facto a standard name):

template&lt;typename T&gt;
struct assertType {
    static constexpr bool v =
        boost::mp11::mp_contains&lt;SupportedTypes, T&gt;::value;
};

答案2

得分: 2

这只有在允许程序化解决方案定义assertType的模板特化或在其模板定义内部使用时才可能。

以下是一个通过在C++11中定义单个assertType部分模板特化的方法:

template <typename>
struct assertType { static constexpr bool v = false; };

template <typename...>
struct TypeList {};

using supportedTypes = TypeList<int, bool, float, long>;

#include <type_traits>

template <typename...>
struct any_of : std::false_type {};

template <typename T, typename U, typename... Us>
struct any_of<T, TypeList<U, Us...>> : any_of<T, TypeList<Us...>> {};

template <typename T, typename... Us>
struct any_of<T, TypeList<T, Us...>> : std::true_type {};

template <typename T>
struct assertType<typename std::enable_if<any_of<T, supportedTypes>::value, T>::type> {
  static constexpr bool v = true;
};

你也可以在类模板定义内部直接使用它,而不是定义部分特化:

// 假设 supportedTypes 和 any_of 已经被定义

template <typename T>
struct assertType { static constexpr bool v = any_of<T, supportedTypes>::value; };

C++17中,使用折叠表达式std::bool_constant可以使any_of的定义变得简单许多:

#include <type_traits>

template <typename...>
struct any_of : std::false_type {};

template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::bool_constant<(... || std::is_same_v<T, Us>)> {};

或者通过使用std::disjunction

#include <type_traits>

template <typename...>
struct any_of : std::false_type {};

template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::disjunction<std::is_same_v<T, Us>...> {};

最后,在C++20中,你可以使用概念来简化assertType的部分模板特化的定义:

// 假设 supportedTypes 和 any_of 已经被定义

template <typename T, typename L>
concept any_of_v = any_of<T, L>::value;

template <typename>
struct assertType { static constexpr bool v = false; };

template <any_of_v<supportedTypes> T>
struct assertType<T> { static constexpr bool v = true; };
英文:

This is only possible if your programmatic solution is allowed to define a template specialization of assertType, or to be used within its template definition.

Here's an approach by defining a single partial template specialization of assertType in [tag:C++11]:

template &lt;typename&gt;
struct assertType { static constexpr bool v = false; };

template &lt;typename...&gt;
struct TypeList {};

using supportedTypes = TypesList&lt;int, bool, float, long&gt;;

#include &lt;type_traits&gt;

template &lt;typename...&gt;
struct any_of : std::false_type {};

template &lt;typename T, typename U, typename... Us&gt;
struct any_of&lt;T, TypeList&lt;U, Us...&gt;&gt; : any_of&lt;T, TypeList&lt;Us...&gt;&gt; {};

template &lt;typename T, typename... Us&gt;
struct any_of&lt;T, TypeList&lt;T, Us...&gt;&gt; : std::true_type {};

template &lt;typename T&gt;
struct assertType&lt;typename std::enable_if&lt;any_of&lt;T, supportedTypes&gt;, T&gt;::type&gt; {
  static constexpr bool v = true;
};

You could also just use it within the class template definition itself rather than defining a partial specialization:

// assuming supportedTypes and any_of have already been defined

template &lt;typename T&gt;
struct assertType { static constexpr bool v = any_of&lt;T, supportedTypes&gt;::value; };

In [tag:C++17] the definition of any_of becomes much simpler with a fold expression and std::bool_constant:

#include &lt;type_traits&gt;

template &lt;typename...&gt;
struct any_of : std::false_type {};

template &lt;typename T, typename... Us&gt;
struct any_of&lt;T, TypeList&lt;Us...&gt;&gt; : std::bool_constant&lt;(... || std::is_same_v&lt;T, Us&gt;)&gt; {};

Or by using std::disjunction:

#include &lt;type_traits&gt;

template &lt;typename...&gt;
struct any_of : std::false_type {};

template &lt;typename T, typename... Us&gt;
struct any_of&lt;T, TypeList&lt;Us...&gt;&gt; : std::disjunction&lt;std::is_same_v&lt;T, Us&gt;...&gt; {};

Finally in [tag:C++20], you can simplify the definition of the assertType partial template specialization using concepts:

// assuming supportedTypes and any_of have already been defined

template &lt;typename T, typename L&gt;
concept any_of_v = any_of&lt;T, L&gt;::value;

template &lt;typename&gt;
struct assertType { static constexpr bool v = false; };

template &lt;any_of_v&lt;supportedTypes&gt; T&gt;
struct assertType&lt;T&gt; { static constexpr bool v = true; };

答案3

得分: 0

除了实例化模板或预处理技巧之外,在C++中没有自动生成声明的方法。每个声明都必须明确写出。

但是,没有必要为列表中的每种类型都进行显式特化。您可以简单地使用一个主模板来进行assertType,并根据列表中的成员设置v成员,或者您可以使用固定数量的部分特化。其他答案提供了执行此操作的方法。

英文:

Except for instantiating a template or preprocessor trickery, there is no way to generate declarations in C++ automatically. Each declaration must be written out explicitly.

But there is no need to have an explicit specialization for each type in the list. You can simply use a single primary template for assertType and set the v member according to membership in the list or you can use a fixed number of partial specializations. The other answers give approaches to do this.

huangapple
  • 本文由 发表于 2023年1月9日 01:37:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75049988.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定