将数组的值解包为可变参数函数的参数
unpacking values of an array as parameters to a variadic function
我正在尝试(在编译时)将整数解压缩为可变参数函数的参数。这个想法是在编译时将这些值打包在数组或 std::index_sequence
(c++14) 中。我尝试使用旧帖子中的一些答案,但我发现示例代码对于我的水平来说不可读。
这是一个简单的示例,其中包含我需要在我正在编写的代码中实现的功能,在本例中尝试使用 std::make_index_sequence
。我不一定需要使用后者。问题是序列的值没有解压缩为可变参数函数的参数:
#include <cstdio>
#include <iostream>
#include <utility>
using namespace std;
void print(const int &val){
cout << val << endl;
}
template<typename ...S> void print(const int &val, const S&... others)
{
print(val);
print(others...);
}
template<size_t n> void printNumbers(){
std::make_index_sequence<n> a;
print(a);
}
int main(){
printNumbers<6>();
}
GCC8 的输出:
tet.cc: In instantiation of ‘void printNumbers() [with long unsigned int n = 6]’:
tet.cc:25:19: required from here
tet.cc:20:8: error: no matching function for call to ‘print(std::make_index_sequence<6>&)’
print(a);
~~~~~^~~
tet.cc:8:6: note: candidate: ‘void print(const int&)’
void print(const int &val){
^~~~~
tet.cc:8:6: note: no known conversion for argument 1 from ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’} to ‘const int&’
tet.cc:12:30: note: candidate: ‘template<class ... S> void print(const int&, const S& ...)’
template<typename ...S> void print(const int &val, const S&... others)
^~~~~
tet.cc:12:30: note: template argument deduction/substitution failed:
tet.cc:20:9: note: cannot convert ‘a’ (type ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’}) to type ‘const int&’
std::make_index_sequence<6>
是以下的别名:
std::integer_sequence<std::size_t, 0, 1, 2, 3, 4, 5>
这是表达式 a
的类型,它是函数调用 print(a)
的参数。您的 print
函数需要单独的值,而不是 std::integer_sequence
.
为了让你的实现工作,你应该首先推导索引,然后才将它们用作 print
:
的参数
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
print(Is...);
}
template <std::size_t N>
void printNumbers()
{
printNumbers(std::make_index_sequence<N>{});
}
在 c++17 中,您可以删除中间的 print
函数,只需说:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
(print(Is), ...);
}
在 c++20 中,您可以创建索引序列并在单个函数中推导其索引:
template <std::size_t N>
void printNumbers()
{
[] <std::size_t... Is> (std::index_sequence<Is...>)
{ (print(Is), ...); }(std::make_index_sequence<N>{});
}
作为 Piotr Skotnicki 回答的补充,我提出了一种 C++14 方法来避免递归 print()
。
不像 C++17 解决方案那么优雅,基于模板折叠,但同样允许避免使用递归(并且考虑到模板递归通常受到编译器的严格限制,因此递归解决方案有效但无效当 N
超出递归限制时)。
您必须像往常一样编写 printNumber()
函数,将 std::make_index_sequence<N>
(继承自 std::index_sequence<0, 1, ...., N-1>
又名 std::integer_sequence<std::size_t, 0, 1, ..., N-1>
)传递给另一个函数
template <std::size_t N>
void printNumbers ()
{ printNumbers2(std::make_index_sequence<N>{}); }
但在 printNumbers2()
中你可以避免调用递归 print()
并且你可以调用 print()
有效调用 std::cout
在未使用数组的初始化中
template <std::size_t ... Is>
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (print(Is), 0)... };
}
您也可以避免在 printNumbers2()
中直接打印两个 print()
函数
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (std::cout << val << std::endl, 0)... };
}
您可以在 C++17/C++20 模板折叠解决方案中执行相同的操作。
在 C++11 中,此解决方案不起作用,但这只是因为 std::make_integer_sequence
和 std::index_sequence
是从 C++11 引入的。
如果您为 std::make_integer_sequence
和 std::index_sequence
编写 C++11 代理项,您也可以将此解决方案改编为 C++11。
我正在尝试(在编译时)将整数解压缩为可变参数函数的参数。这个想法是在编译时将这些值打包在数组或 std::index_sequence
(c++14) 中。我尝试使用旧帖子中的一些答案,但我发现示例代码对于我的水平来说不可读。
这是一个简单的示例,其中包含我需要在我正在编写的代码中实现的功能,在本例中尝试使用 std::make_index_sequence
。我不一定需要使用后者。问题是序列的值没有解压缩为可变参数函数的参数:
#include <cstdio>
#include <iostream>
#include <utility>
using namespace std;
void print(const int &val){
cout << val << endl;
}
template<typename ...S> void print(const int &val, const S&... others)
{
print(val);
print(others...);
}
template<size_t n> void printNumbers(){
std::make_index_sequence<n> a;
print(a);
}
int main(){
printNumbers<6>();
}
GCC8 的输出:
tet.cc: In instantiation of ‘void printNumbers() [with long unsigned int n = 6]’:
tet.cc:25:19: required from here
tet.cc:20:8: error: no matching function for call to ‘print(std::make_index_sequence<6>&)’
print(a);
~~~~~^~~
tet.cc:8:6: note: candidate: ‘void print(const int&)’
void print(const int &val){
^~~~~
tet.cc:8:6: note: no known conversion for argument 1 from ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’} to ‘const int&’
tet.cc:12:30: note: candidate: ‘template<class ... S> void print(const int&, const S& ...)’
template<typename ...S> void print(const int &val, const S&... others)
^~~~~
tet.cc:12:30: note: template argument deduction/substitution failed:
tet.cc:20:9: note: cannot convert ‘a’ (type ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’}) to type ‘const int&’
std::make_index_sequence<6>
是以下的别名:
std::integer_sequence<std::size_t, 0, 1, 2, 3, 4, 5>
这是表达式 a
的类型,它是函数调用 print(a)
的参数。您的 print
函数需要单独的值,而不是 std::integer_sequence
.
为了让你的实现工作,你应该首先推导索引,然后才将它们用作 print
:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
print(Is...);
}
template <std::size_t N>
void printNumbers()
{
printNumbers(std::make_index_sequence<N>{});
}
在 c++17 中,您可以删除中间的 print
函数,只需说:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
(print(Is), ...);
}
在 c++20 中,您可以创建索引序列并在单个函数中推导其索引:
template <std::size_t N>
void printNumbers()
{
[] <std::size_t... Is> (std::index_sequence<Is...>)
{ (print(Is), ...); }(std::make_index_sequence<N>{});
}
作为 Piotr Skotnicki 回答的补充,我提出了一种 C++14 方法来避免递归 print()
。
不像 C++17 解决方案那么优雅,基于模板折叠,但同样允许避免使用递归(并且考虑到模板递归通常受到编译器的严格限制,因此递归解决方案有效但无效当 N
超出递归限制时)。
您必须像往常一样编写 printNumber()
函数,将 std::make_index_sequence<N>
(继承自 std::index_sequence<0, 1, ...., N-1>
又名 std::integer_sequence<std::size_t, 0, 1, ..., N-1>
)传递给另一个函数
template <std::size_t N>
void printNumbers ()
{ printNumbers2(std::make_index_sequence<N>{}); }
但在 printNumbers2()
中你可以避免调用递归 print()
并且你可以调用 print()
有效调用 std::cout
在未使用数组的初始化中
template <std::size_t ... Is>
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (print(Is), 0)... };
}
您也可以避免在 printNumbers2()
print()
函数
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (std::cout << val << std::endl, 0)... };
}
您可以在 C++17/C++20 模板折叠解决方案中执行相同的操作。
在 C++11 中,此解决方案不起作用,但这只是因为 std::make_integer_sequence
和 std::index_sequence
是从 C++11 引入的。
如果您为 std::make_integer_sequence
和 std::index_sequence
编写 C++11 代理项,您也可以将此解决方案改编为 C++11。