两个具有相同大小的可变模板参数
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), ...);
}
我发现你的代码有四个问题
(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_sequence
和 std::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), ...); }
我想实现一个 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), ...);
}
我发现你的代码有四个问题
(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_sequence
和 std::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), ...); }