我想了解C++中的参数解包

I would like to understand parameter unpacking in C++

本文关键字:参数 了解 C++      更新时间:2023-10-16

我试图使用可变参数模板编写我的第一个程序。专门打包延迟函数调用的参数。我最初调用函数指针的假设

如下所示不起作用:
template<typename... params>
void TypeX<params...>::delayed_call()
{
    auto xs = get_saved_args(); // returns tuple
    (*func)(xs...);
}

环顾四周后,我找到了这个答案C++11:我可以从多个参数转到元组,但是我可以从元组到多个参数吗?

用户Kerrek SB的答案有效,并且看起来比其他替代方案更好。不幸的是,我只部分理解它。这是他的回答:

// implementation details, users never invoke these directly
namespace detail
{
    template <typename F, typename Tuple, bool Done, int Total, int... N>
    struct call_impl
    {
        static void call(F f, Tuple && t)
        {
            call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t));
        }
    };
    template <typename F, typename Tuple, int Total, int... N>
    struct call_impl<F, Tuple, true, Total, N...>
    {
        static void call(F f, Tuple && t)
        {
            f(std::get<N>(std::forward<Tuple>(t))...);
        }
    };
}
// user invokes this
template <typename F, typename Tuple>
void call(F f, Tuple && t)
{
    typedef typename std::decay<Tuple>::type ttype;
    detail::call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t));
}

我知道这是一个递归解决方案,一旦达到部分专用版本,它就会终止。不过,我无法理解流程,以及f(std::get<N>(std::forward<Tuple>(t))...);究竟是如何变成一个未打包的电话的。理想情况下,我希望看到从用户可调用函数调用一直到流程的详细描述。

盯着这个看了一会儿后,我终于得出了一些合乎逻辑的东西。这是我目前对这个解包实现的理解。为了清楚起见,我在代码中添加了注释。

template <typename F, typename Tuple, bool Done, int Total, int... N>
struct call_impl
{
    static void call(F f, Tuple && t)
    {
        call_impl<F,
                  Tuple,
                  Total == 1 + sizeof...(N),
                  Total,
                  // This is the tricky part: Initial N... is empty, so
                  // sizeof...(N) is going to be 0, which is fed to next
                  // call_impl. On subsequent invokation of "call" N will
                  // contain a single entry - '0'. All subsequent invokations
                  // will keep adding numbers to N until
                  // Total == 1 + sizeof...(N)
                  // evaluates to true. At that point template
                  // specialization below gets invoked and completes the
                  // recursive chain.
                  N...,
                  sizeof...(N)>::call(f, std::forward<Tuple>(t));
    }             
};                
template <typename F, typename Tuple, int Total, int... N>
struct call_impl<F, Tuple, true, Total, N...>
{
    static void call(F f, Tuple && t)
    {
        // This is the last call in the recusrive chain, so int... N is
        // actually a tuple of numbers (unless the function being invoked takes
        // no arguments)
        // Ellipsis(...) causes the line below to be expanded to however many
        // numbers the int... N contains:
        // f(std::get<0>(std::forward<Tuple>(t)),
        //   std::get<1>(std::forward<Tuple>(t)),
        //   std::get<2>(std::forward<Tuple>(t))
        //   ...
        // );
        f(std::get<N>(std::forward<Tuple>(t))...);
    }   
};      
// user invokes this
template <typename F, typename Tuple>
void call(F f, Tuple && t)
{
    // Strip all qualifiers from Tuple: constness, volatility, reference
    // and return the typedef of underfyling Tuple type
    typedef typename std::decay<Tuple>::type ttype;
    // ttype should now be a plain tuple type, so tuple_size will return its
    // size
    // IMPORTANT: The int... N is not given below, which drives the recursive
    // compilation
    call_impl<F,
              Tuple,
              0 == std::tuple_size<ttype>::value, // for no argument functions this is
              // always true, which doesn't trigger recursive call_impl, but
              // immediately drops to partially specialized call_impl, which
              // invokes the given function.
              std::tuple_size<ttype>::value
              /*int... N would go here, but nothing is supplied */
             >::call(f, std::forward<Tuple>(t));
}