我如何“扩展”一个编译时的std :: array到参数包? [英] How do I "expand" a compile-time std::array into a parameter pack?
问题描述
我想使用部分模板特化,以便将数组(在编译时创建的)分解为一个由其值组成的参数包(以与我在代码中定义的其他结构进行交互)。以下(我的第一次尝试)未编译
I'd like to use partial template specialization in order to 'break down' an array (which is created at compile time) into a parameter pack composed of its values (to interface with other structures I define in my code). The following (my first attempt) is not compiling
#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 ...}
。据我了解,如果非类型参数非常依赖模板参数,则无法在部分模板专业化中提供非类型参数。因此,我尝试通过将值包含在以下类型中来解决此问题:
because the template argument std::array<long unsigned int, sizeof... (A)>{A ...}
must not involve template parameters. As I understood it, it is not possible to provide non type parameters in a partial template specialization if they non-trivially depend on template parameters. Hence I attempted to work around this issue by containing the value in a type:
#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;
}
但是,在这里,当我尝试使用部分模板时,专业化失败它;上面的定义与我使用它的方式不匹配,我不确定为什么。它失败并显示以下错误:
However, here, the partial template specialization fails when I'm trying to use it; the definition above does not match the way I use it, and I am unsure why. It fails with the following error:
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;
是否可以解决此问题/使用其他方法将数组作为参数包进行接口?
Is it possible to work around this / use a different approach in order to interface the array as parameter pack?
我使用的是 g ++-10.0(GCC)10.0.1 20200124(实验性)
并通过 g ++ -std = c ++ 2a file.cc
进行编译,因此需要c ++ 2a,因为我使用的是非类型模板参数。
I use g++-10.0 (GCC) 10.0.1 20200124 (experimental)
and compile via g++ -std=c++2a file.cc
, c++2a is required because I use non-type template parameters.
在我的真实代码中,我得到了一个结构,该结构依赖于(除其他外)参数包(1)。如果我能够使用数组(2)(我在另一段代码中获得的非类型模板参数)与该结构进行交互,这将是很好的,如以下代码所示。
In my real code I have got a structure which depends on -- among others -- a parameter pack (1). It would be nice if I were able to use an array (2) (which I have got in another piece of my code as a non-type template argument) to interface with that structure, as sketched in the code below.
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
的辅助结构,我可以使用 array
实例化其中的类型定义为 FromStruct :: type
可为 toBeUsed
提供正确的参数,类似于此示例,在此我想用std :: tuple组成的类型来做。
My Attempt is to write a helper struct as above FromStruct
, which I can instantiate with an array
in which I have a typedef FromStruct::type
that provides toBeUsed
with the correct arguments, similar to this example, which does what I want to do here with the types a std::tuple is composed of.
此处我链接了简化的用法示例(第二个代码块)。
here I link the simplified usage example (2nd code block).
推荐答案
一种C ++ 20方法
请参见 OP自己的答案,或者,对于可能具有指导性但更冗长(且不太有用)的方法,此答案的修订2 。
A C++20 approach
See OP's own answer or, for possibly instructive but more verbose (and less useful) approach, revision 2 of this answer.
(此答案最初包含使用次要C ++ 20功能的方法(默认情况下可能会默认构造没有捕获的lambda),但受原始答案的启发,OP利用了这一事实提供了更为整洁的C ++ 20方法 constexpr
std :: array
属于可以作为非类型模板传递的文字类C ++ 20中的参数(在其 :: value_type
受限制的情况下),结合对索引序列使用部分特殊化来将数组解压缩为参数包。但是,最初的答案利用了将 std :: array
包装到 constexpr
lambda(> = C ++ 17)作为 constexpr
(特定) std :: array
创建者,而不是实际的 constexpr
std :: array
。有关此方法的详细信息,请请参阅此答案的修订2 )
(This answer originally contained an approach using a minor C++20 feature (that a lambda without any captures may be default constructed), but inspired by the original answer the OP provided a much neater C++20 approach making use of the fact that a constexpr
std::array
falls under the kind of literal class that may be passed as a non-type template parameter in C++20 (given restraints on its ::value_type
), combined with using partial specialization over the index sequence used to unpack the array into a parameter pack. This original answer, however, made use of a technique of wrapping std::array
into a constexpr
lambda (>=C++17) which acted as a constexpr
(specific) std::array
creator instead of an actual constexpr
std::array
. For details regarding this approach, see revision 2 of this answer)
遵循OP的整洁方法,以下是对它对C ++ 17的修改,使用非类型的左值引用模板参数在编译时提供对数组的引用以构造目标数组。
Following OP's neat approach, below follows an adaption of it for C++17, using a non-type lvalue reference template parameter to provide, at compile time, a reference to the array to the array to struct target.
#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作为替代方法。
Note that this approach places a restriction on the std::array
instance in that it needs to have static storage duration. If one wants to avoid this, using a constexpr
lambda which generates the array may be used as an alternative.
这篇关于我如何“扩展”一个编译时的std :: array到参数包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!