如何在参数包不是最后一个参数的C++中编写可变参数模板函数?

How do I write a variadic template function in C++ where the parameter pack is not the last parameter?

本文关键字:参数 变参 函数 C++ 包不 最后一个      更新时间:2023-10-16

我正在尝试使用可变参数模板函数,其中参数包不是列表中的最后一个参数。请注意,有两个递归调用 - 一个在包前面删除参数,另一个调用在包后面丢弃参数。

  • 我的编译器似乎是: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_sequencestd::make_index_sequence)。

--编辑--

卡莱斯说。

对这里发生的事情进行一些解释会有所帮助。我对第二次超载的身体缺乏I0感到困惑。

我尝试解释。

假设称为BlenderH()的递归版本(第二个重载),std::index_sequence值中有一个索引列表。说 5、6、7 和 8;所以I05Is...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