如何在参数包不是最后一个参数的C++中编写可变参数模板函数?
How do I write a variadic template function in C++ where the parameter pack is not the last parameter?
我正在尝试使用可变参数模板函数,其中参数包不是列表中的最后一个参数。请注意,有两个递归调用 - 一个在包前面删除参数,另一个调用在包后面丢弃参数。
- 我的编译器似乎是:Apple LLVM 版本 8.1.0 (clang-802.0.42)
- 如果我能让它工作,下面的所有 int 都将是一个新的 T 模板参数。
使用...如果Blender的呼叫站点不能干净。在这种情况下,我可以自己扩展Blender的几个重载。我真的宁愿不诉诸这个。我希望我只是错过了一些东西。
int Blender( double t, int i)
{
return i;
}
template <typename ...Args>
int Blender( double t, int first, Args... more, int last)
{
return (1-t)*Blender(t, first, more...) + t*Blender(t, more..., last);
}
static void tryit()
{
Blender(.5, 23, 42, 89); //doesn't compile
}
我提出了另一种解决方案,与问题中的代码真正不同,我认为效率更高(仅在一个std::array
上创建;通过模板索引序列获得的blender)
#include <array>
#include <utility>
#include <iostream>
template <typename T, std::size_t I0>
int blenderH (double t, T const & arr, std::index_sequence<I0> const &)
{ return arr[I0]; }
template <typename T, std::size_t I0, std::size_t ... Is>
auto blenderH (double t, T const & arr,
std::index_sequence<I0, Is...> const &)
-> std::enable_if_t<0U != sizeof...(Is), int>
{ return (1-t) * blenderH(t, arr, std::index_sequence<(Is-1U)...>{})
+ t * blenderH(t, arr, std::index_sequence<Is...>{}); }
template <typename ... Args>
int blender (double t, Args ... as)
{
static constexpr auto size = sizeof...(Args);
return blenderH(t, std::array<int, size>{ { as... } },
std::make_index_sequence<size>{});
}
int main()
{ std::cout << blender(.3, 23, 42, 89) << std::endl; }
不幸的是,这个解决方案也从C++14开始工作(std::index_sequence
和std::make_index_sequence
)。
--编辑--
卡莱斯说。
对这里发生的事情进行一些解释会有所帮助。我对第二次超载的身体缺乏
I0
感到困惑。
我尝试解释。
假设称为BlenderH()
的递归版本(第二个重载),std::index_sequence
值中有一个索引列表。说 5、6、7 和 8;所以I0
是5
,Is...
是6, 7, 8
.
我们必须首先使用索引递归调用blenderH()
5, 6, 7
,然后使用索引6, 7, 8
。
我们可以避免使用I0
(5
),因为
5, 6, 7
是通过6, 7, 8
将每个值减少 1 获得的(因此第一次递归调用的std::index_sequence<(Is-1U)...>
)并且
6, 7, 8
无需修改即可Is...
(因此第二个中的std::index_sequence<Is...>
。
从实际的角度来看,I0
被宣布只是被丢弃;没有必要使用它。
template<std::size_t I>
using count = std::integral_constant<std::size_t, I>;
namespace details {
template<class...Args, std::size_t N,
typename std::enable_if<sizeof...(Args)==N, bool>::type = true
>
int Blender( count<N> drop, double t, int i, Args...args ) {
return i;
}
template<class...Args, std::size_t N,
typename std::enable_if<sizeof...(Args)!=N, bool>::type = true
>
int Blender( count<N> drop, double t, int i, Args...args ) {
return (1-t)*Blender( count<N+1>{}, t, i, args... ) + t*Blender( count<N>{}, t, args... );
}
}
template <typename ...Args>
int Blender( double t, int first, Args... more)
{
return details::Blender( count<0>{}, first, more... );
}
static void tryit()
{
Blender(.5, 23, 42, 89); //doesn't compile
}
在这里count<N>
计算最后要忽略的参数数。
两个details
重载涵盖了N
等于包中的参数数量(因此我们还剩下 1 个参数)的情况,以及当它不等于时。 他们被派往使用SFINAE。
一旦您意识到限制在于类型包推导而不是函数调用,这实际上非常容易。 我们只需要一种方法来使用我们可以推断的类型包并显式传递它,避免在需要拉出最后一个参数的函数中扣除:
int Blender( double t, int i)
{
return i;
}
template <typename ...Args>
int Blender( double t, int first, Args... more);
template <typename ...Args>
int BlenderWithoutLast( double t, Args... more, int last)
{
return Blender(t, more...);
}
template <typename ...Args>
int Blender( double t, int first, Args... more)
{
return (1-t)*BlenderWithoutLast<Args...>(t, first, more...) + t*Blender(t, more...);
// all the magic happens here ^^^^^
}
现在您的测试用例可以编译并运行
#include <iostream>
int main()
{
std::cout << Blender(.5, 23, 42, 89);
}
对我来说,这与叮当和--std=c++11
- 在不传递参数数量且只有3个点的情况下,如何使用变差函数
- 如何使用可变参数模板强制转换每个变体类型
- 关于如何在具有单个参数的变体构造中选择替代方案?
- 调用参数排列不变函数 f(i++, i++)
- 参数归纳与标准::变体
- 模板化回调参数的逆变,如 C# 中的逆变
- 如何在没有参数包的情况下编写变差函数
- 通过具有嵌套类的工厂类获取多个变异类模板参数包
- 获取模板参数的成员变量值列表
- 保留短 lambda 用作函数的中间参数,使用 clang 格式保持不变
- 如何定义变体<x,y,z>提取模板参数的子类型
- 正确对齐内存模板,参数顺序不变
- 递归中不同参数类型的变元模板函数
- 通过函数指针传递给变差函数的参数会更改其值
- 提升预定义为带有参数的全局 lambda 的变体访问者
- 使用可变参数模板参数提升变体访问者
- boost ::变体 - 为什么模板参数比const字符串参数具有更高的优先级
- 将变参数包中的值加载到临时数组中
- 使用额外参数提升变体访客
- 正在将动态数组元素解析为参数?(变音符)