两个具有相同大小的可变模板参数

Two variadic template arguments with the same size

我想实现一个 Print 函数,它可以这样工作:

Print<1, 3>("Hello", "World");

我希望它会打印一次 "Hello" 并且 "World" 3 times.I 想知道如何实现它。 下面是我的傻代码,编译的时候当然失败了:

template <unsigned int n, unsigned int ...n_next,
          typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
    for(int i = 0; i < n; i++)
    {
        std::cout << t << " ";
    }
    std::cout << std::endl;

    Print<n_next..., ts...>(ts...);
}

template <unsigned int n, typename T>
void Print(T & t)
{
    for(int i = 0; i < n; i++)
    {
        std::cout << t << " ";
    }
    std::cout << std::endl;
}

这将使它:

template <unsigned int n, typename T>
void Print(T&& t)
{
    for(int i = 0; i < n; i++)
    {
        std::cout << std::forward<T>(t) << " ";
    }
    std::cout << std::endl;
}

template <std::size_t Idx1, std::size_t... Idx, class T, class... Ts>
void Print(T&& t, Ts&& ... ts) {
    Print<Idx1>(std::forward<T>(t));

    using expand = int[];
    (void)expand{(Print<Idx>(std::forward<Ts>(ts)), 0) ...};
}

您只需交换两个重载的声明并删除递归调用中的 ts... 模板参数即可使您的代码正常工作:

template <unsigned int n, typename T>
void Print(T & t)
{
    for(unsigned int i = 0; i < n; i++)
    {
        std::cout << t << " ";
    }
    std::cout << std::endl;
}

template <unsigned int n, unsigned int ...n_next,
          typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
    for(unsigned int i = 0; i < n; i++)
    {
        std::cout << t << " ";
    }
    std::cout << std::endl;

    Print<n_next...>(ts...);
}

(另外,与符号一致)
Demo

此外,您不需要复制打印部分,只需调用另一个重载:

template <unsigned int n, typename T>
void Print(T & t)
{
    for(unsigned int i = 0; i < n; i++)
    {
        std::cout << t << " ";
    }
    std::cout << std::endl;
}

template <unsigned int n, unsigned int ...n_next,
          typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
    Print<n>(t);
    Print<n_next...>(ts...);
}

或者,如果您可以将 C++17 用于折叠表达式,则可以执行以下操作(如果需要,使用转发引用和 std::forward):

template<typename T>
void Print(unsigned int n, T& t)
{
    for(unsigned int i = 0; i < n; i++)
    {
        std::cout << t << " ";
    }
    std::cout << std::endl;
}

template<unsigned int... Ns, typename... Ts>
void Print(Ts&... ts)
{
    (Print(Ns, ts), ...);
}

Demo

我发现你的代码有四个问题

(1) 递归调用

Print<n_next..., ts...>(ts...);

是错误的,因为您必须在模板参数列表中使用 Ts... 类型,而不是 ts...

Print<n_next..., Ts...>(ts...);

或者,更好(因为允许我接下来解释的技巧)而不解释类型

Print<n_next...>(ts...);

(2) 如果您收到 const 引用值

则更好
template <unsigned int n, unsigned int ...n_next,
          typename T, typename ...Ts>
void Print(T const & t, Ts ... ts)
// ..........^^^^^

否则你不能像下面这样用常量值调用Print()

Print<1u, 3u>(1, "world");

(3) 最好在 for 循环中为索引使用无符号值,因为您必须使用无符号值测试它们(小问题)

// ..VVVVVVVV
for (unsigned int i = 0; i < n; i++)

(4) 您必须将 Print() 的基本情况(仅接收一个值的情况)放在 之前 递归情况。

我建议用

代替它们
template <typename = void>
void Print ()
 { }

因为,通过这种方式,所有打印都是在递归版本中完成的,您不需要在两个不同的函数中重复相同的代码(但您必须调用 Print<n_next...>(ts...); 递归。

所以我建议修改你的代码如下

#include <iostream>

template <typename = void>
void Print ()
 { }

template <unsigned int n, unsigned int ...n_next,
          typename T, typename ...Ts>
void Print(T const & t, Ts ... ts)
{
    for(auto i = 0u; i < n; i++)
     {
       std::cout << t << " ";
     }

    std::cout << std::endl;

    Print<n_next...>(ts...);
}

int main ()
 {
   Print<1u, 3u>("hello", "world");
 }

我还提出了一个完全不同的解决方案,完全避免了递归和 for() 循环。

在未使用的 C 风格数组的初始化中模拟 C++14 中的模板折叠。

首先是主要的 Print(),它扩展了调用 Print_h() 辅助函数的可变参数列表,将值和列表(索引序列)传递给它对应于每个值的迭代次数

template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
 {
   using unused=int[];

   (void)unused { 0, (Print_h(std::make_index_sequence<Ns>{}, ts), 0)... };
 }

接下来是使用相同技巧进行多次打印的辅助函数

template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
 {
   using unused=std::size_t[];

   (void)unused { 0, (std::cout << t << " ", Is)... };

   std::cout << std::endl;
 }

以下是完整编译C++14的例子

#include <utility>
#include <iostream>

template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
 {
   using unused=std::size_t[];

   (void)unused { 0, (std::cout << t << " ", Is)... };

   std::cout << std::endl;
 }

template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
 {
   using unused=int[];

   (void)unused { 0, (Print_h(std::make_index_sequence<Ns>{}, ts), 0)... };
 }

int main ()
 {
   Print<1u, 3u>("hello", "world");
 }

如果你不能使用 C++14 而只能使用 C++11,那么开发 std::make_index_sequencestd::index_sequence 的替代品并不难(两者都只能从 C++14 获得).

显然在 C++17 中你可以使用模板折叠简化函数如下

template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
 {    
   ((std::cout << t << " ", (void)Is), ...);

   std::cout << std::endl;
 }

template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
 { (Print_h(std::make_index_sequence<Ns>{}, ts), ...); }