【问题标题】:Use variadic args to call subsequent method使用可变参数调用后续方法
【发布时间】:2021-08-23 14:01:34
【问题描述】:

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

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

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

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

    

【问题讨论】:

  • 一般情况下不可能。 F 可以例如是一个具有多个operator() 重载的类,采用不同数量的参数。或者 operator() 本身就是一个可变参数模板。应该可以做类似“找到f 可以调用的args... 的最长前缀”。

标签: c++17 variadic-templates


【解决方案1】:

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

正如 Igor Tandetnik 在评论中指出的那样,这通常是不可能的,因为 f func 可以是可变参数,可以是具有模板 operator() 的 lambda,或者具有多个类/结构的实例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);
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多