使用可变参数调用后续方法

Use variadic args to call subsequent method

假设我有接受 1 个可调用 F 和可变参数列表的函数:

template <class F, class ...Args>
void combine(F& f, Args&& ... args)

F 可以有从 1 到 sizeof...(Args) 的任意数量的参数。所以我需要使用 c++17 来调用 F,然后将未使用的参数放入 std::cout,就像那样

{
    std::invoke(f, [some way to restrict args]);
    std::cout << [The rest of args] << "\n";
}

    

最大的问题是检测 F 的参数数量。

正如 Igor Tandetnik 在评论中指出的那样,这通常是不可能的,因为 f 函数可以是可变的,可以是具有模板 operator() 的 lambda 或实例一个 class/struct 和多个 operator().

无论如何...在某些情况下,您可以使用以下内容检测参数的数量

template <typename T>
struct num_args 
   : public num_args<decltype(&T::operator())>
 { };
 
template <typename R, typename ... Args>
struct num_args<R(*)(Args...)>
   : public std::integral_constant<std::size_t, sizeof...(Args)>
 { };

// other specialization follows

此时,您可以编写 combine() 来调用具有适当索引和参数元组的辅助函数

template <typename F, typename ... Args>
void combine (F & func, Args && ... as)
 { 
   constexpr auto n1 { num_args<F>::value };
   constexpr auto n2 { sizeof...(Args) - n1 };

   combine_helper(std::make_index_sequence<n1>{},
                  std::make_index_sequence<n2>{},
                  func,
                  std::forward_as_tuple(std::forward<Args>(as)...));
 }

辅助函数可以简单如下

template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
void combine_helper (std::index_sequence<Is1...>,
                     std::index_sequence<Is2...>,
                     F & func, T && t)
 {
   func(std::get<Is1>(std::forward<T>(t))...);
   (std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
      << '\n';
 }

下面是一个完整的编译C++17的例子

#include <iostream>
#include <utility>
#include <tuple>

template <typename T>
struct num_args 
   : public num_args<decltype(&T::operator())>
 { };
 
template <typename R, typename ... Args>
struct num_args<R(*)(Args...)>
   : public std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <typename R, typename ... Args>
struct num_args<R(Args...)>
   : public std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <typename R, typename C, typename ... Args>
struct num_args<R(C::*)(Args...)>
   : public std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <typename R, typename C, typename ... Args>
struct num_args<R(C::*)(Args...) const>
   : public std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
void combine_helper (std::index_sequence<Is1...>,
                     std::index_sequence<Is2...>,
                     F & func, T && t)
 {
   func(std::get<Is1>(std::forward<T>(t))...);
   (std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
      << '\n';
 }

template <typename F, typename ... Args>
void combine (F & func, Args && ... as)
 { 
   constexpr auto n1 { num_args<F>::value };
   constexpr auto n2 { sizeof...(Args) - n1 };

   combine_helper(std::make_index_sequence<n1>{},
                  std::make_index_sequence<n2>{},
                  func,
                  std::forward_as_tuple(std::forward<Args>(as)...));
 }


void func_1 (int a, int b, int c)
 { std::cout << "the product is: " << a*b*c << '\n'; }

int main()
 {
   auto extra  { 100 };
   auto func_2 { [&](int a, int b, int c, int d)
    { std::cout << "the extra sum is: " << extra+a+b+c+d << '\n'; } };

   combine(func_1, 1, 2, 3, 4, 5, 6);
   combine(func_2, 1, 2, 3, 4, 5, 6);
 }