c++ 如何将编译时std::数组“扩展”到参数包中?

gab6jxml  于 2023-03-05  发布在  其他
关注(0)|答案(3)|浏览(128)

我想使用部分模板专门化来"分解"一个数组(在编译时创建)为一个由它的值组成的参数包(与我在代码中定义的其他结构接口)。

#include <array>

template <typename T, auto k> struct K;
template <typename T, std::size_t... A> struct K<T, std::array<std::size_t, sizeof...(A)>{A...}> {};

因为模板参数std::array<long unsigned int, sizeof... (A)>{A ...}must not involve template parameters。据我所知,如果非类型参数非常依赖于模板参数,那么在部分模板专用化中提供非类型参数是不可能的。因此,我尝试通过在类型中包含值来解决这个问题:

#include <array>

template <auto f> struct any_type;

template <typename T, typename array_wrapper> struct FromArr;
template <typename T, std::size_t... A>
struct FromArr<T, any_type<std::array<std::size_t, sizeof...(A)>{A...}>> {};

int main() {
  FromArr<int, any_type<std::array<std::size_t, 2>{1, 2}>> d;
  (void) d;
}

但是,在这里,当我尝试使用部分模板专用化时,它失败了;上面的定义与我使用它的方式不匹配,我不确定原因。2它失败了,并出现以下错误:

file.cc: In function ‘int main()’:
file.cc:10:55: error: aggregate ‘FromArr<int, Any<std::array<long unsigned int, 2>{std::__array_traits<long unsigned int, 2>::_Type{1, 2}}> > d’ has incomplete type and cannot be defined
  10  |   FromArr<int, Any<std::array<std::size_t, 2>{1, 2}>> d;

是否可以解决这个问题/使用不同的方法将数组作为参数包进行接口?

使用的编译器

我使用g++-10.0 (GCC) 10.0.1 20200124 (experimental)并通过g++ -std=c++2a file.cc编译,因为我使用非类型模板参数,所以需要c ++2a。

编辑:

描述如何处理数组

在我的真实代码中,我有一个依赖于参数包(1)的结构,如果我能够使用数组(2)(我在另一段代码中将其作为非类型模板参数)来与该结构接口,如下面的代码所示,那就太好了。

template <int... s> struct toBeUsed;                               // (1)
template <std::size_t s, std::array<int, s> arr> struct Consumer { // (2)
    toBeUsed<arr> instance; // This is what I would like to do
}

我的尝试是写一个如上FromStruct的helper结构体,我可以用一个array示例化它,在array中我有一个typedefFromStruct::type,它为toBeUsed提供正确的参数,类似于this example,它做了我想在这里对std::tuple组成的类型所做的事情。

链接到示例

here我链接了简化用法示例(第2个代码块)。

kqqjbcuj

kqqjbcuj1#

受@dfri的启发,我将她/他的解决方案转换为可以省略函数的版本,但只使用一个结构体,该结构体使用std::integer_sequence的部分模板特化,其他人可能也会感兴趣:

template <auto arr, template <typename X, X...> typename Consumer,
          typename IS = decltype(std::make_index_sequence<arr.size()>())> struct Generator;

template <auto arr, template <typename X, X...> typename Consumer, std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...>> {
  using type = Consumer<typename decltype(arr)::value_type, arr[I]...>;
};

用法完整示例:

#include <array>

/// Structure which wants to consume the array via a parameter pack.
template <typename StructuralType, StructuralType... s> struct ConsumerStruct {
  constexpr auto operator()() const { return std::array{s...}; }
};

/// Solution
template <auto arr, template <typename X, X...> typename Consumer,
          typename IS = decltype(std::make_index_sequence<arr.size()>())> struct Generator;

template <auto arr, template <typename X, X...> typename Consumer, std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...>> {
  using type = Consumer<typename decltype(arr)::value_type, arr[I]...>;
};

/// Helper typename
template <auto arr, template <typename T, T...> typename Consumer>
using Generator_t = typename Generator<arr, Consumer>::type;

// Usage
int main() {
  constexpr auto tup = std::array<int, 3>{{1, 5, 42}};
  constexpr Generator_t<tup, ConsumerStruct> tt;
  static_assert(tt() == tup);
  return 0;
}
j8yoct9x

j8yoct9x2#

一种C++20方法

请参见OP's own answer,或者revision 2 of this answer(可能有指导意义,但更冗长(也不太有用))。

一种C++17方法

  • (这个答案最初包含了一个使用C20的次要特性的方法(没有任何捕获的lambda可能是默认构造的),但受最初答案的启发,OP提供了一种更简洁的C20方法,它利用了constexprstd::array福尔斯可以在C20中作为非类型模板参数传递的文本类这一事实(给定对::value_type的约束),结合使用索引序列的部分专用化,用于将数组解包到参数包中。利用将std::array Package 成constexpr λ的技术(〉=C17),它充当constexpr(特定的)std::array创建者,而不是实际的constexprstd::array。有关此方法的详细信息,请参见see revision 2 of this answer)*

遵循OP简洁的方法,下面是它在C++17中的修改,使用非类型左值引用模板参数在编译时提供对数组的引用。

#include <array>
#include <cstdlib>
#include <tuple>
#include <type_traits>
#include <utility>

// Parameter pack structure (concrete target for generator below).
template <typename StructuralType, StructuralType... s>
struct ConsumerStruct
{
    // Use tuple equality testing for testing correctness.
    constexpr auto operator()() const { return std::tuple{s...}; }
};

// Generator: FROM std::array TO Consumer.
template <const auto& arr,
          template <typename T, T...> typename Consumer,
          typename Indices = std::make_index_sequence<arr.size()> >
struct Generator;

template <const auto& arr,
          template <typename T, T...> typename Consumer,
          std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...> >
{
    using type =
        Consumer<typename std::remove_cv<typename std::remove_reference<
                     decltype(arr)>::type>::type::value_type,
                 arr[I]...>;
};

// Helper.
template <const auto& arr, template <typename T, T...> typename Consumer>
using Generator_t = typename Generator<arr, Consumer>::type;

// Example usage.
int main()
{
    // As we want to use the address of the constexpr std::array at compile
    // time, it needs to have static storage duration.
    static constexpr std::array<int, 3> arr{{1, 5, 42}};
    constexpr Generator_t<arr, ConsumerStruct> cs;
    static_assert(cs() == std::tuple{1, 5, 42});
    return 0;
}

注意,这种方法对std::array示例有一个限制,即它需要有静态的存储持续时间。如果想避免这种情况,可以使用constexpr lambda来生成数组。

wn9m85ua

wn9m85ua3#

您还可以执行以下操作:

template<typename T, T... Values>
struct ResultStruct
{};

template<template<typename T, T... Values> typename ResultType, typename ElementType, size_t N, std::array<ElementType, N> Array, size_t... Is>
auto as_pack_helper(std::integer_sequence<size_t, Is...>) -> ResultType<ElementType, Array[Is]...>;

template<template<typename T, T... Values> typename ResultType, std::array Array>
using as_pack_t = decltype(as_pack_helper<ResultType, typename decltype(Array)::value_type, Array.size(), Array>(std::make_index_sequence<Array.size()>{}));

static_assert(std::is_same_v<as_pack_t<ResultStruct, std::array<int, 3>{1, 2, 3}>, ResultStruct<int, 1, 2, 3>>);

它的工作原理与解决方案非常相似,但是使用decltype和函数定义来生成结果类型!

相关问题