【问题标题】:Call function with part of variadic arguments使用部分可变参数调用函数
【发布时间】:2017-09-02 12:58:56
【问题描述】:

考虑一下我有以下几点:

void bar(int a, int b)
{
}   

template<typename F, typename... Args>
void foo(F function, Args... args>
{
    function(args...);
}

我想有某种方法只将必要数量的参数传递给函数,这样我就可以执行以下操作,这将导致以 1、2 作为参数的 bar 调用被丢弃3. 不知道传入的函数类型 F 需要多少参数。

foo(bar, 1, 2, 3);
foo([](int a, int b){}, 1, 2, 3);

当我尝试使用以下函数特征时:

namespace detail
{
    template<typename F, std::size_t... Is, class Tup>
    void call_discard_impl(F&& func, std::index_sequence<Is...>, Tup&& tup) 
    {
        std::forward<F>(func)(std::get<Is>(tup)...);
    }
}

template<typename F, typename... Args>
void call_discard(F&& func, Args&&... args)
{
    detail::call_discard_impl(std::forward<F>(func),
        std::make_index_sequence<function_traits<F>::num_args>{},
        std::forward_as_tuple(args...));
}

我明白了:

error C2510: 'F': left of '::' must be a class/struct/union
error C2065: '()': undeclared identifier
error C2955: 'function_traits': use of class template requires template argument list

开启:

template <typename F>
struct function_traits : public function_traits<decltype(&F::operator())>
{}

我确实得到了不需要函数特征的成员函数版本:

namespace detail
{
    template<typename O, typename R, typename... FunArgs, std::size_t... Is, class Tup>
    void call_discard_impl(O* obj, R(O::*mem_func)(FunArgs...), std::index_sequence<Is...>, Tup&& tup)
    {
        ((*obj).*mem_func)(std::get<Is>(tup)...);
    }
}

template<typename O, typename R, typename... FunArgs, typename... Args>
void call_discard(O* obj, R(O::*mem_func)(FunArgs...), Args&&... args)
{
    detail::call_discard_impl(obj, mem_func,
        std::make_index_sequence<sizeof...(FunArgs)>{},
        std::forward_as_tuple(args...));
}

【问题讨论】:

    标签: c++11 templates lambda callable


    【解决方案1】:

    首先,使用以下代码来查找 lambda 或函数引用的元数:

    template <typename T>
    struct function_traits : public function_traits<decltype(&T::operator())>
    {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) const>
    {
        using result_type = ReturnType;
        using arg_tuple = std::tuple<Args...>;
        static constexpr auto arity = sizeof...(Args);
    };
    
    template <typename R, typename ... Args>
    struct function_traits<R(&)(Args...)>
    {
        using result_type = R;
        using arg_tuple = std::tuple<Args...>;
        static constexpr auto arity = sizeof...(Args);
    };
    

    接下来,您使用元组包转发可变参数,并且您只扩展了函数的数量:

    template<typename F, std::size_t... Is, class T>
    void foo_impl(F && f, std::index_sequence<Is...>, T && tuple) {
        std::forward<F>(f)(std::get<Is>(tuple)...);
    }
    
    template<typename F, typename... Args>
    void foo(F && f, Args&&... args) {
        foo_impl(std::forward<F>(f),
                 std::make_index_sequence<function_traits<F>::arity>{},
                 std::forward_as_tuple(args...) );
    }
    

    实时示例:http://coliru.stacked-crooked.com/a/3ca5df7b55c427b8

    【讨论】:

    • 我不能让它对 lambdas 起作用,因为它说 operator() 没有为 function_traits 的类型 T 定义。 '()' 未声明的标识符
    • @AndreasLoanjoe 无法复制:coliru.stacked-crooked.com/a/6c01bab04cae5143。你能解释一下吗?
    【解决方案2】:

    首先,我们需要一个函数来检索函数所需的数字或参数。这是使用function_traits 完成的:

    template <class F>
    constexpr std::size_t nb_args() {
       return utils::function_traits<F>::arity;
    }
    

    std::index_sequence 的帮助下,我们只调度nb_args&lt;F&gt;() 第一个参数:

    template<typename F, std::size_t... Is, class Tup>
    void foo_impl(F && f, std::index_sequence<Is...>, Tup && tup) {
        std::forward<F>(f)( std::get<Is>(tup)... );
    }
    
    template<typename F, typename... Args>
    void foo(F && f, Args&&... args) {
        foo_impl(std::forward<F>(f),
                 std::make_index_sequence<nb_args<F>()>{},
                 std::forward_as_tuple(args...) );
    }
    

    Demo

    【讨论】:

    • 不,它只适用于函数指针。编辑:我不确定它是否适用于任何事情。它在 constexpr 上下文中使用带有非 constexpr 参数的 constexpr 函数,这是不合法的。
    • @AndreasLoanjoe 要使用 lambda,您可以使用来自 herefunction_traits
    • 我在看这篇文章,但在这里他们可以 decltype lambda 的类型,因为 lambda 类型在范围内是已知的。但是对于 F,我不能像这样 decltype 吗?
    • 这个答案甚至不适用于函数指针,甚至无法编译。
    • @AndreasLoanjoe 你不必使用 decltype,你已经有了F
    【解决方案3】:

    简单且难以扩展的解决方案是创建一个包装器,该包装器将使用所有参数调用,但只会使用其中的前几个。

    template<typename F, typename... Args>
    void foo(F function, Args... args)
    {
        // with proper forwarding if needed
        auto lambda = [](auto fnc, auto first, auto second, auto...)
        {
            fnc(first, second);
        };
        lambda(function, args...);
    }
    

    【讨论】:

      【解决方案4】:

      这是一个适用于任何std::invoke 接受的解决方案,它使用最少 个可能的参数调用重载。

      template <typename F, typename Args, std::size_t... In>
      decltype(auto) invoke_front_impl(F&& f, Args&& args, std::index_sequence<In...>)
      {
          if constexpr (std::is_invocable_v<F&&, std::tuple_element_t<In, Args>...>) {
              return std::invoke(std::forward<F>(f), std::get<In>(std::move(args))...);
          } else {
              return invoke_front_impl(
                  std::forward<F>(f),
                  std::move(args),
                  std::make_index_sequence<sizeof...(In) + 1>());
          }
      }
      
      template <typename F, typename... Args>
      decltype(auto) invoke_front(F&& f, Args&&... args)
      {
          return invoke_front_impl(
              std::forward<F>(f),
              std::forward_as_tuple(std::forward<Args>(args)...),
              std::make_index_sequence<0>());
      }
      

      Demo on Wandbox

      【讨论】:

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