如何将编译时 std::array“扩展”为参数包?

mut*_*oid 7 c++ templates non-type c++20

我想使用部分模板专门化来将数组(在编译时创建)“分解”为由其值组成的参数包(以与我在代码中定义的其他结构交互)。以下(我的第一次尝试)未编译

\n\n
#include <array>\n\ntemplate <typename T, auto k> struct K;\ntemplate <typename T, std::size_t... A> struct K<T, std::array<std::size_t, sizeof...(A)>{A...}> {};\n
Run Code Online (Sandbox Code Playgroud)\n\n

因为模板实参std::array<long unsigned int, sizeof... (A)>{A ...} 一定不能涉及模板形参。据我了解,如果非类型参数非常依赖于模板参数,则不可能在部分模板专业化中提供非类型参数。因此,我尝试通过将值包含在类型中来解决这个问题:

\n\n
#include <array>\n\ntemplate <auto f> struct any_type;\n\ntemplate <typename T, typename array_wrapper> struct FromArr;\ntemplate <typename T, std::size_t... A>\nstruct FromArr<T, any_type<std::array<std::size_t, sizeof...(A)>{A...}>> {};\n\nint main() {\n  FromArr<int, any_type<std::array<std::size_t, 2>{1, 2}>> d;\n  (void) d;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,在这里,当我尝试使用它时,部分模板专业化失败了;上面的定义与我使用它的方式不符,我不确定为什么。它失败并出现以下错误:

\n\n
file.cc: In function \xe2\x80\x98int main()\xe2\x80\x99:\nfile.cc:10:55: error: aggregate \xe2\x80\x98FromArr<int, Any<std::array<long unsigned int, 2>{std::__array_traits<long unsigned int, 2>::_Type{1, 2}}> > d\xe2\x80\x99 has incomplete type and cannot be defined\n  10  |   FromArr<int, Any<std::array<std::size_t, 2>{1, 2}>> d;\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

使用的编译器

\n\n

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

\n\n

编辑:

\n\n

描述如何处理数组

\n\n

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

\n\n
file.cc: In function \xe2\x80\x98int main()\xe2\x80\x99:\nfile.cc:10:55: error: aggregate \xe2\x80\x98FromArr<int, Any<std::array<long unsigned int, 2>{std::__array_traits<long unsigned int, 2>::_Type{1, 2}}> > d\xe2\x80\x99 has incomplete type and cannot be defined\n  10  |   FromArr<int, Any<std::array<std::size_t, 2>{1, 2}>> d;\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的尝试是编写一个如上所述的辅助结构FromStruct,我可以用 an 实例化它array,其中我有一个FromStruct::type提供toBeUsed正确参数的 typedef,类似于此示例,它执行我想要在此处使用 std:: 类型执行的操作元组是由.

\n\n

链接到示例

\n\n

在这里我链接了简化的使用示例(第二个代码块)。

\n

mut*_*oid 6

受到 @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]...>;
};
Run Code Online (Sandbox Code Playgroud)

完整的用法示例:

#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;
}
Run Code Online (Sandbox Code Playgroud)


dfr*_*fri 2

C++20 方法

请参阅OP自己的答案,或者,对于可能有指导意义但更详细(且不太有用)的方法,请参阅此答案的修订版2

C++17 方法

(这个答案最初包含一种使用次要 C++20 功能的方法(可以默认构造没有任何捕获的 lambda),但受原始答案的启发,OP 提供了一种更简洁的 C++20 方法,利用这一事实aconstexpr std::array属于可以在 C++20 中作为非类型模板参数传递的文字类(对其进行限制::value_type),并结合对用于将数组解包到参数包的索引序列使用部分特化.然而,这个原始答案使用了包装std::arrayconstexprlambda (>=C++17) 中的技术,该技术充当constexpr(特定)std::array创建者而不是实际的constexpr std::array。有关此方法的详细信息,请参阅此答案的修订版 2

遵循 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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,此方法对实例有限制std::array,因为它需要具有静态存储持续时间。如果想避免这种情况,constexpr可以使用生成数组的 lambda 作为替代方案。