简体   繁体   English

c++中使用了部分模板参数包的function如何调用?

[英]How to call function that use part of template parameter pack in c++?

I have following problem:我有以下问题:

I want to create variadic template class, that must call lambda, that may take first N parameters of template parameter pack (N may vary from 0 to the size of parameters pack and differs for different lambda).我想创建可变参数模板 class,它必须调用 lambda,它可能采用模板参数包的前 N 个参数(N 可能从 0 到参数包的大小不等,并且因不同的 lambda 而异)。

I think, that recursive template helper function, that will check if lambda is invokable, starting with all parameters, should be the best variant, but how can I pass all but last parameters from parameters pack to it and is it possible at all?我认为,递归模板助手 function 将检查 lambda 是否可调用,从所有参数开始,应该是最好的变体,但我如何将参数包中除最后一个参数之外的所有参数传递给它,这是否可能?

At the moment I see it as something like this:目前我认为它是这样的:

template< typename Callable, typename ...Args >
void Call( Callable& cb, Args... args )
{
   using Function = std::remove_reference_t< Callable >;
   if constexpr( std::is_invocable_v< Function, Args... > )
      cb( args... );
   else if constexpr( sizeof...( args ) == 0 )
      cb();
   else
   {
      // Here I want to create shortened_args, that are all args but last one.
      // Call( cb, shortened_args... );
   }
}

UPD: I didn't mention it first, but I need C++17 solution. UPD:我没有先提到它,但我需要 C++17 解决方案。

You might use std::index_sequence as helper:您可以使用std::index_sequence作为助手:

template <typename Callable, typename Tuple, std::size_t... Is>
constexpr std::size_t is_invocable_args(std::index_sequence<Is...>)
{
    return std::is_invocable_v<Callable, std::tuple_element_t<Is, Tuple>...>;
}

// Helper to know max number of args to take from tuple
template <typename Callable, typename Tuple, std::size_t... Is>
constexpr std::size_t max_invocable_args(std::index_sequence<Is...>)
{
    std::array<bool, sizeof...(Is)> a = {
        is_invocable_args<Callable, Tuple>(std::make_index_sequence<Is>{})...
        };
    auto rit = std::find(a.rbegin(), a.rend(), true);
    if (rit == a.rend()) throw "no valid call";
    return std::distance(a.begin(), rit.base()) - 1;
}

template <std::size_t N, typename Callable, typename Tuple>
constexpr std::size_t max_invocable_args()
{
    return max_invocable_args<Callable, Tuple>(std::make_index_sequence<N + 1{});
}

// Call taking some element from tuple
template< typename Callable, std::size_t... Is, typename Tuple >
void Call_Tuple_impl(Callable cb, std::index_sequence<Is...>, Tuple&& args )
{
    std::invoke(cb, std::get<Is>(std::forward<Tuple>(args))...);
}

template< typename Callable, typename ...Args >
void Call( Callable cb, Args... args )
{
    constexpr std::size_t max_arg_count = max_invocable_args<sizeof...(Args), Callable, std::tuple<Args...>>();

    Call_Tuple_impl(cb, std::make_index_sequence<max_arg_count>{}, std::forward_as_tuple(std::forward<Args>(args)...));
}

C++20 Demo C++20 演示
C++17 Demo with rewritten constexpr find . C++17重写constexpr find的演示。

Break it into two pieces.把它分成两部分。 First, determine how many arguments. Then invoke with that many arguments.首先,确定有多少 arguments。然后用那么多 arguments 调用。

template<class Tup, std::size_t...Is>
auto slice_tuple(Tup tup, std::index_sequence<Is...>){
  using std::get;
  return std::make_tuple(get<Is>(std::forward<Tup>(tup))...);
}
template<std::size_t N, class Tup>
auto front_of_tuple(Tup tup){
  return slice_tuple(std::forward<Tup>(tup), std::make_index_sequence<N>{});
}

pack up the args into a tuple, get the front N, then std apply.将args打包成元组,获取前面的N,然后std apply。

To perfect forward/avoid copies, use reference wrappers.要完善转发/避免复制,请使用参考包装器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM