单个功能中有多个参数包?

时间:2018-02-12 04:23:16

标签: c++ variadic-templates variadic-functions c++17

我试图创建一个带有两个参数包对象的函数。有两个模板化的基类,我想将派生类的实例传递给这个函数。考虑这个例子。

template <int N>
struct First {};

template <int N>
struct Second {};

// there are a few of these
struct FirstImpl : First<5> {};
struct SecondImpl : Second<7> {};

template <int... firstInts, int... secondInts>
void function(float f, First<firstInts> &... first, Second<secondInts> &... second) {
  // ...
}

我想做的就是像这样打电话给function

FirstImpl firstImpl;
OtherFirstImpl otherFirstImpl;
SecondImpl secondImpl;
OtherSecondImpl otherSecondImpl;
function(9.5f, firstImpl, otherFirstImpl, secondImpl, otherSecondImpl);

但这个例子不会编译。编译器似乎试图将所有内容打包到第二个参数包中并失败,因为FirstImpl无法隐式转换Second<N>

我该如何解决这个问题?

4 个答案:

答案 0 :(得分:2)

使用两个可变参数包来定义一些东西几乎是不可能的。一旦遇到可变参数包,它就会消耗所有剩余的参数,不会留下第二个包的碎屑。

但是,正如我所提到的,在很多情况下你可以使用元组,而在C ++ 17中使用演绎指南,调用约定只比其他情况稍长。

-std=c++17模式下使用gcc 7.3.1进行测试:

#include <tuple>

template <int N>
struct First {};

template <int N>
struct Second {};


template <int... firstInts, int... secondInts>
void function(std::tuple<First<firstInts>...> a,
          std::tuple<Second<secondInts>...> b)
{
}

int main(int, char* [])
{
    function( std::tuple{ First<4>{}, First<3>{} },
          std::tuple{ Second<1>{}, Second<4>{} });
}

这是基本的想法。在你的情况下,你有子类来处理,所以需要一个更复杂的方法,可能初始声明两个元组只是一个通用的std::tuple< First...>std::tuple<Second...>,还有一些额外的模板 - fu 。可能需要让FirstSecond在类成员声明中声明自己的类型,然后让前面提到的template-fu查找类成员,并找出它正在处理的超类。

但以上是如何从单个可变参数列表中指定两组参数的基本思路,然后再进一步使用它......

答案 1 :(得分:1)

让我们的第一个代码成为一个变量模板,用于确定某个类型是否来自First

template <int N>
constexpr std::true_type is_first(First<N> const &) { return {}; }
template <int N>
constexpr std::false_type is_first(Second<N> const &) { return {}; }

template <class T>
constexpr bool is_first_v = decltype( is_first(std::declval<T>()) )::value;

结构Split收集FirstSecond类型的索引:

template <class, class, class, std::size_t I = 0> struct Split;

template <
    std::size_t... FirstInts,
    std::size_t... SecondInts,
    std::size_t N
>
struct Split<
    std::index_sequence<FirstInts...>,
    std::index_sequence<SecondInts...>,
    std::tuple<>,
    N
> {
    using firsts = std::index_sequence<FirstInts...>;
    using seconds = std::index_sequence<SecondInts...>;
};

template <
    std::size_t... FirstInts,
    std::size_t... SecondInts,
    std::size_t I, 
    typename T,
    typename... Tail
>
struct Split<
    std::index_sequence<FirstInts...>,
    std::index_sequence<SecondInts...>,
    std::tuple<T, Tail...>,
    I
> : std::conditional_t<
    is_first_v<T>,
    Split<std::index_sequence<FirstInts..., I>,
          std::index_sequence<SecondInts...>,
          std::tuple<Tail...>,
          I + 1
    >,
    Split<std::index_sequence<FirstInts...>,
          std::index_sequence<SecondInts..., I>,
          std::tuple<Tail...>,
          I + 1
    >
> {};

就像我在评论中告诉你的那样,将成员value添加到FirstSecond(或继承自std:integral_constant),这样我们就可以编写以下内容:

template <std::size_t... FirstIdx, std::size_t... SecondIdx, typename Tuple>
void function_impl(float f, std::index_sequence<FirstIdx...>, std::index_sequence<SecondIdx...>, Tuple const & tup) {
    ((std::cout << "firstInts: ") << ... << std::get<FirstIdx>(tup).value) << '\n';
    ((std::cout << "secondInts: ") << ... << std::get<SecondIdx>(tup).value) << '\n';
    // your implementation
}

template <class... Args>
void function(float f, Args&&... args)  {
    using split = Split<std::index_sequence<>,std::index_sequence<>, std::tuple<std::decay_t<Args>...>>;
    function_impl(f, typename split::firsts{}, typename split::seconds{}, std::forward_as_tuple(args...));
}

<强> Demo

答案 2 :(得分:0)

为什么不简单地将类本身作为模板参数传递?像这样:

template <int N>
struct First {};

template <int N>
struct Second {};

// there are a few of these
struct FirstImpl : First<5> {};
struct SecondImpl : Second<7> {};

template <typename FirstSpec, typename SecondSpec>
void function(float f, FirstSpec & first, SecondSpec & second) {
  // ...
}

答案 3 :(得分:0)

不完全是你问的但是...你可以使用一个变量模板模板is_admin: true容器(int,在下面的例子中)统一这两个列表,然后检测每个参数, if是Cnt还是First(请参阅使用Second

以下是一个完整的工作示例

std::is_same_v