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

时间:2014-11-15 21:12:56

标签: c++ templates c++11

我试图使用可变参数模板编写我的第一个程序。具体包装延迟函数调用的参数。我最初假设调用如下所示的函数指针不起作用:

template<typename... params>
void TypeX<params...>::delayed_call()
{
    auto xs = get_saved_args(); // returns tuple
    (*func)(xs...);
}

环顾四周后,我找到了这个答案C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?

用户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))...);到底是如何变成一个解压缩的调用。理想情况下,我希望从用户可调用函数调用中看到流的详细描述。

1 个答案:

答案 0 :(得分:1)

盯着这一段时间后,我终于找到了合乎逻辑的东西。这是我对这个解包实现的理解。为了清楚起见,我在代码中添加了注释。

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));
}