解开数组的值作为可变参数的参数

时间:2018-11-24 09:03:07

标签: c++ c++11 c++14 variadic-templates template-meta-programming

我正在尝试(在编译时)将整数解压缩为可变参数的参数。想法是在编译时将这些值打包在数组或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&’

2 个答案:

答案 0 :(得分:4)

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>{});
}

中,您可以删除中间的print函数,然后说:

template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
    (print(Is), ...);
}

中,您既可以创建索引序列,也可以在单个函数中推导其索引:

template <std::size_t N>
void printNumbers()
{
    [] <std::size_t... Is> (std::index_sequence<Is...>)
    { (print(Is), ...); }(std::make_index_sequence<N>{});
}

DEMO

答案 1 :(得分:1)

作为Piotr Skotnicki答案的附录,我提出了一种C ++ 14方式来避免递归print()

不如基于模板折叠的C ++ 17解决方案那么优雅,但同样可以避免使用递归(并且考虑到模板递归通常严格受编译器的限制,因此递归解决方案有效,但当{ {1}}超出了递归限制。

您必须像往常一样编写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()的{​​{1}}

print()

您还可以避免直接在std::cout中打印两个template <std::size_t ... Is> void printNumbers2 (std::index_sequence<Is...>) { using unused = int[]; (void)unused { 0, (print(Is), 0)... }; } 函数

print()

您可以在C ++ 17 / C ++ 20模板折叠解决方案中执行相同的操作。

在C ++ 11中,此解决方案不起作用,仅是因为从C ++ 11中引入了printNumbers2()void printNumbers2 (std::index_sequence<Is...>) { using unused = int[]; (void)unused { 0, (std::cout << val << std::endl, 0)... }; }

如果您为std::make_integer_sequencestd::index_sequence编写C ++ 11代理,则也可以使此解决方案适应C ++ 11。