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

时间:2017-12-17 19:47:31

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

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

  • 我的编译器似乎是: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
}

3 个答案:

答案 0 :(得分:1)

我提出另一种解决方案,与问题中的代码完全不同,我认为效率更高(仅在一个std::array上创建;通过模板索引序列获得的混合器)

#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)。

- 编辑 -

Caleth说。

  

对此处发生的事情的一些解释会有所帮助。我对第二次超载体内缺少I0感到困惑。

我尝试解释。

假设被称为BlenderH()的递归版本(第二个重载),其中包含std::index_sequence值中的索引列表。说5,6,7和8;因此I05Is...6, 7, 8

我们必须首先使用索引blenderH()递归调用5, 6, 7,然后使用6, 7, 8递归。

我们可以避免使用I05),因为

  • 5, 6, 76, 7, 8获得每个值减1(所以第一次递归调用的std::index_sequence<(Is-1U)...>

  • 6, 7, 8未经修改Is...(因此第二个中的std::index_sequence<Is...>

从实际的角度来看,I0被宣布只被丢弃;没有必要使用它。

答案 1 :(得分:0)

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。

答案 2 :(得分:0)

一旦你意识到限制是在类型包推导中而不是在函数调用中,这实际上非常简单。我们只需要一种方法来使用我们可以推导出来的类型包并明确地传递它,避免在需要拉出最后一个参数的函数上进行推导:

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

对我而言,这适用于clang和--std=c++11