C++ 一个函数中有多个参数包?

C++ 一个函数中有多个参数包?,c++,variadic-templates,variadic-functions,c++17,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> {}; templ

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

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) {
  // ...
}
但是这个例子无法编译。编译器似乎试图将所有内容都打包到第二个参数包中,但失败了,因为
FirstImpl
无法隐式转换
second


如何解决这个问题?

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

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) {
  // ...
}
模板
结构优先{};
模板
结构第二{};
//其中有一些
struct FirstImpl:First{};
结构SecondImpl:Second{};
模板
无效函数(浮动f、第一规格和第一规格、第二规格和第二规格){
// ...
}

不完全是你要求的,但是。。。您可以使用可变模板
int
容器(
Cnt
,在下面的示例中)统一这两个列表,然后为每个参数检测第一个
或第二个
(请参阅
std::is__v
的用法)

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

#include <string>
#include <vector>
#include <iostream>
#include <type_traits>

template <int>
struct First {};

template <int>
struct Second {};

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

template <template <int> class ... Cnt, int... Ints>
void function (float f, Cnt<Ints> & ... args)
 {
    (std::cout << ... << std::is_same_v<Cnt<Ints>, First<Ints>>);
 }

int main()
 {
   FirstImpl firstImpl;
   FirstImpl otherFirstImpl;
   SecondImpl secondImpl;
   SecondImpl otherSecondImpl;
   function(9.5f, firstImpl, otherFirstImpl, secondImpl, otherSecondImpl);
 }
#包括
#包括
#包括
#包括
模板
结构优先{};
模板
结构第二{};
//其中有一些
struct FirstImpl:First{};
结构SecondImpl:Second{};
模板
空函数(浮点f、Cnt和…参数)
{

(std::cout几乎不可能用两个可变参数包定义某个东西。一旦遇到可变参数包,它喜欢消耗所有剩余的参数,不留下任何碎屑供第二个包食用

然而,正如我所提到的,在许多情况下,您可以使用元组,并且使用C++17中的演绎指南,调用约定只比其他方式稍微长一点

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

#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>{} });
}
#包括
模板
结构优先{};
模板
结构第二{};
模板
void函数(std::元组a,
std::元组b)
{
}
int main(int,char*[])
{
函数(std::tuple{First{},First{},
std::元组{Second{},Second{});
}
这就是基本思想。在您的例子中,您需要处理子类,因此需要一种更复杂的方法,可能两个元组的初始声明只是一个泛型
std::tuple
std::tuple
,以及一些额外的模板fu。可能需要有
First
s第二步
在类成员声明中声明自己的类型,然后让前面提到的模板查找类成员,并找出它正在处理的超类


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

让我们首先编写一个变量模板,该模板确定一个类型是否从
首先派生出

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;
就像我在评论中告诉你的那样,在
第一个
第二个
中添加一个成员
(或继承自),这允许我们编写以下内容:

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...));
}
模板
void function_impl(float f,std::index_sequence,std::index_sequence,Tuple const&tup){

((std::我是否需要传递多个基类的多个实例。我还需要基类的int参数实际上,编译器可能正在尝试将所有内容填充到第一个参数包中,而不是第二个参数包中。一旦遇到一个参数包,它喜欢使用看到的所有其他内容;而第一个参数包不会对于第二个,我没有立即看到任何实现这种调用约定的方法,但是如果您愿意使用其他调用参数,您可能可以使用两个变量元组而不是两个变量包来实现某些功能。哦,我应该想到这一点!将其放入一个答案中,我会接受它。您需要吗函数中的索引?您可以在
First
Second
中添加一个
static constepr value=N;
成员。奥尼尔是的,我需要在同一表达式中扩展firstInts包和firstPack。奥尼尔,您有什么解决方案吗?std::tuple不能从调用站点中删除?这几乎适用于我的情况但是我需要传递FirstImpl并将其作为第一个&。我的编译器说“无法将'First&'与'FirstImpl'匹配。我是否应该找到某种方法将此函数调用分成两部分?我确实希望避免这种情况,因为我必须向调用方公开一些实现细节,但看起来我没有选择余地。
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
    >
> {};
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...));
}