简体   繁体   English

如何将元组扩展为可变参数模板函数的参数?

[英]How do I expand a tuple into variadic template function's arguments?

Consider the case of a templated function with variadic template arguments:考虑带有可变参数模板参数的模板化函数的情况:

template<typename Tret, typename... T> Tret func(const T&... t);

Now, I have a tuple t of values.现在,我有一个值元组t How do I call func() using the tuple values as arguments?如何使用元组值作为参数调用func() I've read about the bind() function object, with call() function, and also the apply() function in different some now-obsolete documents.我已经在不同的一些现已过时的文档中阅读了有关bind()函数对象、 call()函数以及apply()函数的内容。 The GNU GCC 4.4 implementation seems to have a call() function in the bind() class, but there is very little documentation on the subject. GNU GCC 4.4 实现似乎在bind()类中有一个call()函数,但是关于这个主题的文档很少。

Some people suggest hand-written recursive hacks, but the true value of variadic template arguments is to be able to use them in cases like above.有些人建议手写递归黑客,但可变参数模板参数的真正价值是能够在上述情况下使用它们。

Does anyone have a solution to is, or hint on where to read about it?有没有人有解决方案,或暗示在哪里阅读它?

In C++17 you can do this:在 C++17 中,你可以这样做:

std::apply(the_function, the_tuple);

This already works in Clang++ 3.9, using std::experimental::apply.这已经适用于 Clang++ 3.9,使用 std::experimental::apply。

Responding to the comment saying that this won't work if the_function is templated, the following is a work-around:回应评论说如果the_function被模板化,这将不起作用,以下是一种解决方法:

#include <tuple>

template <typename T, typename U> void my_func(T &&t, U &&u) {}

int main(int argc, char *argv[argc]) {

  std::tuple<int, float> my_tuple;

  std::apply([](auto &&... args) { my_func(args...); }, my_tuple);

  return 0;
}

This work around is a simplified solution to the general problem of passing overload sets and function template where a function would be expected.此变通方法是对传递重载集和函数模板的一般问题的简化解决方案。 The general solution (one that is taking care of perfect-forwarding, constexpr-ness, and noexcept-ness) is presented here: https://blog.tartanllama.xyz/passing-overload-sets/ .通用解决方案(一种处理完美转发、constexpr-ness 和 noexcept-ness 的解决方案)在此处提供: https ://blog.tartanllama.xyz/passing-overload-sets/。

Here's my code if anyone is interested如果有人感兴趣,这是我的代码

Basically at compile time the compiler will recursively unroll all arguments in various inclusive function calls <N> -> calls <N-1> -> calls ... -> calls <0> which is the last one and the compiler will optimize away the various intermediate function calls to only keep the last one which is the equivalent of func(arg1, arg2, arg3, ...)基本上在编译时编译器将递归展开各种包含函数调用中的所有参数 <N> -> 调用 <N-1> -> 调用 ... -> 调用 <0> 这是最后一个,编译器将优化掉各种中间函数调用只保留最后一个,相当于 func(arg1, arg2, arg3, ...)

Provided are 2 versions, one for a function called on an object and the other for a static function.提供了 2 个版本,一个用于在对象上调用的函数,另一个用于静态函数。

#include <tr1/tuple>

/**
 * Object Function Tuple Argument Unpacking
 *
 * This recursive template unpacks the tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @tparam N Number of tuple arguments to unroll
 *
 * @ingroup g_util_tuple
 */
template < uint N >
struct apply_obj_func
{
  template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( T* pObj,
                          void (T::*f)( ArgsF... ),
                          const std::tr1::tuple<ArgsT...>& t,
                          Args... args )
  {
    apply_obj_func<N-1>::applyTuple( pObj, f, t, std::tr1::get<N-1>( t ), args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Object Function Tuple Argument Unpacking End Point
 *
 * This recursive template unpacks the tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @ingroup g_util_tuple
 */
template <>
struct apply_obj_func<0>
{
  template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( T* pObj,
                          void (T::*f)( ArgsF... ),
                          const std::tr1::tuple<ArgsT...>& /* t */,
                          Args... args )
  {
    (pObj->*f)( args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Object Function Call Forwarding Using Tuple Pack Parameters
 */
// Actual apply function
template < typename T, typename... ArgsF, typename... ArgsT >
void applyTuple( T* pObj,
                 void (T::*f)( ArgsF... ),
                 std::tr1::tuple<ArgsT...> const& t )
{
   apply_obj_func<sizeof...(ArgsT)>::applyTuple( pObj, f, t );
}

//-----------------------------------------------------------------------------

/**
 * Static Function Tuple Argument Unpacking
 *
 * This recursive template unpacks the tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @tparam N Number of tuple arguments to unroll
 *
 * @ingroup g_util_tuple
 */
template < uint N >
struct apply_func
{
  template < typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( void (*f)( ArgsF... ),
                          const std::tr1::tuple<ArgsT...>& t,
                          Args... args )
  {
    apply_func<N-1>::applyTuple( f, t, std::tr1::get<N-1>( t ), args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Static Function Tuple Argument Unpacking End Point
 *
 * This recursive template unpacks the tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @ingroup g_util_tuple
 */
template <>
struct apply_func<0>
{
  template < typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( void (*f)( ArgsF... ),
                          const std::tr1::tuple<ArgsT...>& /* t */,
                          Args... args )
  {
    f( args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Static Function Call Forwarding Using Tuple Pack Parameters
 */
// Actual apply function
template < typename... ArgsF, typename... ArgsT >
void applyTuple( void (*f)(ArgsF...),
                 std::tr1::tuple<ArgsT...> const& t )
{
   apply_func<sizeof...(ArgsT)>::applyTuple( f, t );
}

// ***************************************
// Usage
// ***************************************

template < typename T, typename... Args >
class Message : public IMessage
{

  typedef void (T::*F)( Args... args );

public:

  Message( const std::string& name,
           T& obj,
           F pFunc,
           Args... args );

private:

  virtual void doDispatch( );

  T*  pObj_;
  F   pFunc_;
  std::tr1::tuple<Args...> args_;
};

//-----------------------------------------------------------------------------

template < typename T, typename... Args >
Message<T, Args...>::Message( const std::string& name,
                              T& obj,
                              F pFunc,
                              Args... args )
: IMessage( name ),
  pObj_( &obj ),
  pFunc_( pFunc ),
  args_( std::forward<Args>(args)... )
{

}

//-----------------------------------------------------------------------------

template < typename T, typename... Args >
void Message<T, Args...>::doDispatch( )
{
  try
  {
    applyTuple( pObj_, pFunc_, args_ );
  }
  catch ( std::exception& e )
  {

  }
}

In C++ there is many ways of expanding/unpacking tuple and apply those tuple elements to a variadic template function.在 C++ 中,有很多方法可以扩展/解包元组并将这些元组元素应用于可变参数模板函数。 Here is a small helper class which creates index array.这是一个创建索引数组的小助手类。 It is used a lot in template metaprogramming:它在模板元编程中被大量使用:

// ------------- UTILITY---------------
template<int...> struct index_tuple{}; 

template<int I, typename IndexTuple, typename... Types> 
struct make_indexes_impl; 

template<int I, int... Indexes, typename T, typename ... Types> 
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> 
{ 
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; 
}; 

template<int I, int... Indexes> 
struct make_indexes_impl<I, index_tuple<Indexes...> > 
{ 
    typedef index_tuple<Indexes...> type; 
}; 

template<typename ... Types> 
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> 
{}; 

Now the code which does the job is not that big:现在完成这项工作的代码并不是那么大:

 // ----------UNPACK TUPLE AND APPLY TO FUNCTION ---------
#include <tuple>
#include <iostream> 

using namespace std;

template<class Ret, class... Args, int... Indexes > 
Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup) 
{ 
    return pf( forward<Args>( get<Indexes>(tup))... ); 
} 

template<class Ret, class ... Args> 
Ret apply(Ret (*pf)(Args...), const tuple<Args...>&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup));
}

template<class Ret, class ... Args> 
Ret apply(Ret (*pf)(Args...), tuple<Args...>&&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}

Test is shown bellow:测试如下图所示:

// --------------------- TEST ------------------
void one(int i, double d)
{
    std::cout << "function one(" << i << ", " << d << ");\n";
}
int two(int i)
{
    std::cout << "function two(" << i << ");\n";
    return i;
}

int main()
{
    std::tuple<int, double> tup(23, 4.5);
    apply(one, tup);

    int d = apply(two, std::make_tuple(2));    

    return 0;
}

I'm not big expert in other languages, but I guess that if these languages do not have such functionality in their menu, there is no way to do that.我不是其他语言的大专家,但我想如果这些语言的菜单中没有这样的功能,就没有办法做到这一点。 At least with C++ you can, and I think it is not so much complicated...至少使用 C++ 可以,而且我认为它并没有那么复杂......

I find this to be the most elegant solution (and it is optimally forwarded):我发现这是最优雅的解决方案(并且是最佳转发):

#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

template<size_t N>
struct Apply {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T && t, A &&... a)
        -> decltype(Apply<N-1>::apply(
            ::std::forward<F>(f), ::std::forward<T>(t),
            ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
        ))
    {
        return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
            ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
        );
    }
};

template<>
struct Apply<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T &&, A &&... a)
        -> decltype(::std::forward<F>(f)(::std::forward<A>(a)...))
    {
        return ::std::forward<F>(f)(::std::forward<A>(a)...);
    }
};

template<typename F, typename T>
inline auto apply(F && f, T && t)
    -> decltype(Apply< ::std::tuple_size<
        typename ::std::decay<T>::type
    >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)))
{
    return Apply< ::std::tuple_size<
        typename ::std::decay<T>::type
    >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}

Example usage:用法示例:

void foo(int i, bool b);

std::tuple<int, bool> t = make_tuple(20, false);

void m()
{
    apply(&foo, t);
}

Unfortunately GCC (4.6 at least) fails to compile this with "sorry, unimplemented: mangling overload" (which simply means that the compiler doesn't yet fully implement the C++11 spec), and since it uses variadic templates, it wont work in MSVC, so it is more or less useless.不幸的是,GCC(至少 4.6)无法用“抱歉,未实现:重载”(这只是意味着编译器尚未完全实现 C++11 规范)编译它,并且由于它使用可变参数模板,因此不会在 MSVC 中工作,所以它或多或少没用。 However, once there is a compiler that supports the spec, it will be the best approach IMHO.但是,一旦有支持规范的编译器,恕我直言,这将是最好的方法。 (Note: it isn't that hard to modify this so that you can work around the deficiencies in GCC, or to implement it with Boost Preprocessor, but it ruins the elegance, so this is the version I am posting.) (注意:修改它以便您可以解决 GCC 中的缺陷,或者使用 Boost Preprocessor 实现它并不难,但它破坏了优雅,所以这是我发布的版本。)

GCC 4.7 now supports this code just fine. GCC 4.7 现在支持这个代码就好了。

Edit: Added forward around actual function call to support rvalue reference form *this in case you are using clang (or if anybody else actually gets around to adding it).编辑:在实际函数调用周围添加前向以支持右值引用形式 *this 以防您使用 clang(或者如果其他人实际上开始添加它)。

Edit: Added missing forward around the function object in the non-member apply function's body.编辑:在非成员应用函数主体中的函数对象周围添加了缺少的前向。 Thanks to pheedbaq for pointing out that it was missing.感谢 pheedbaq 指出它丢失了。

Edit: And here is the C++14 version just since it is so much nicer (doesn't actually compile yet):编辑:这里是 C++14 版本,因为它更好(实际上还没有编译):

#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

template<size_t N>
struct Apply {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T && t, A &&... a) {
        return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
            ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
        );
    }
};

template<>
struct Apply<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T &&, A &&... a) {
        return ::std::forward<F>(f)(::std::forward<A>(a)...);
    }
};

template<typename F, typename T>
inline auto apply(F && f, T && t) {
    return Apply< ::std::tuple_size< ::std::decay_t<T>
      >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}

Here is a version for member functions (not tested very much!):这是成员函数的一个版本(没有经过太多测试!):

using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution.

template<size_t N>
struct ApplyMember
{
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply(C&& c, F&& f, T&& t, A&&... a) ->
        decltype(ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...))
    {
        return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...);
    }
};

template<>
struct ApplyMember<0>
{
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply(C&& c, F&& f, T&&, A&&... a) ->
        decltype((forward<C>(c)->*forward<F>(f))(forward<A>(a)...))
    {
        return (forward<C>(c)->*forward<F>(f))(forward<A>(a)...);
    }
};

// C is the class, F is the member function, T is the tuple.
template<typename C, typename F, typename T>
inline auto apply(C&& c, F&& f, T&& t) ->
    decltype(ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t)))
{
    return ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t));
}
// Example:

class MyClass
{
public:
    void foo(int i, bool b);
};

MyClass mc;

std::tuple<int, bool> t = make_tuple(20, false);

void m()
{
    apply(&mc, &MyClass::foo, t);
}
template<typename F, typename Tuple, std::size_t ... I>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template<typename F, typename Tuple>
auto apply(F&& f, Tuple&& t) {
    using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
    return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
}

This is adapted from the C++14 draft using index_sequence.这是使用 index_sequence 从 C++14 草案改编的。 I might propose to have apply in a future standard (TS).我可能会建议在未来的标准 (TS) 中应用。

All this implementations are good.所有这些实现都很好。 But due to use of pointer to member function compiler often cannot inline the target function call (at least gcc 4.8 can't, no matter what Why gcc can't inline function pointers that can be determined? )但是由于使用了指向成员函数的指针,编译器经常无法内联目标函数调用(至少gcc 4.8不能,无论什么gcc为什么不能内联可以确定的函数指针?

But things changes if send pointer to member function as template arguments, not as function params:但是如果将指向成员函数的指针作为模板参数而不是函数参数发送,事情就会发生变化:

/// from https://stackoverflow.com/a/9288547/1559666
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

template<typename TT>
using makeSeq = typename gens< std::tuple_size< typename std::decay<TT>::type >::value >::type;


// deduce function return type
template<class ...Args>
struct fn_type;

template<class ...Args>
struct fn_type< std::tuple<Args...> >{

    // will not be called
    template<class Self, class Fn>
    static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){
        //return (self.*f)(Args()...);
        return NULL;
    }
};

template<class Self, class ...Args>
struct APPLY_TUPLE{};

template<class Self, class ...Args>
struct APPLY_TUPLE<Self, std::tuple<Args...>>{
    Self &self;
    APPLY_TUPLE(Self &self): self(self){}

    template<class T, T (Self::* f)(Args...),  class Tuple>
    void delayed_call(Tuple &&list){
        caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() );
    }

    template<class T, T (Self::* f)(Args...), class Tuple, int ...S>
    void caller(Tuple &&list, const seq<S...>){
        (self.*f)( std::get<S>(forward<Tuple>(list))... );
    }
};

#define type_of(val) typename decay<decltype(val)>::type

#define apply_tuple(obj, fname, tuple) \
    APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
            decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
            &decay<decltype(obj)>::type::fname \
            > \
            (tuple);

And ussage:和用法:

struct DelayedCall
{  
    void call_me(int a, int b, int c){
        std::cout << a+b+c;
    }

    void fire(){
        tuple<int,int,int> list = make_tuple(1,2,3);
        apply_tuple(*this, call_me, list); // even simpler than previous implementations
    }
};

Proof of inlinable http://goo.gl/5UqVnC内联证明http://goo.gl/5UqVnC


With small changes, we can "overload" apply_tuple :通过小的更改,我们可以“重载” apply_tuple

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)

#define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple)
#define apply_tuple3(obj, fname, tuple) \
    APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
            decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
            &decay<decltype(obj)>::type::fname \
            /* ,decltype(tuple) */> \
            (tuple);
#define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__)

...

apply_tuple(obj, call_me, list);
apply_tuple(call_me, list);       // call this->call_me(list....)

Plus this is the only one solution which works with templated functions.此外,这是唯一一种适用于模板化函数的解决方案。

1) if you have a readymade parameter_pack structure as function argument, you can just use std::tie like this: 1) 如果你有一个现成的 parameter_pack 结构作为函数参数,你可以像这样使用 std::tie :

template <class... Args>
void tie_func(std::tuple<Args...> t, Args&... args)
{
 std::tie<Args...>(args...) = t;
}

int main()
{
 std::tuple<int, double, std::string> t(2, 3.3, "abc");

 int i;
 double d;
 std::string s;

 tie_func(t, i, d, s);

 std::cout << i << " " << d << " " << s << std::endl;
}

2) if you don't have a readymade parampack arg, you'll have to unwind the tuple like this 2) 如果您没有现成的 parampack arg,则必须像这样展开元组

#include <tuple>
#include <functional>
#include <iostream>



template<int N>
struct apply_wrap {
    template<typename R, typename... TupleArgs, typename... UnpackedArgs>
    static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>& t, UnpackedArgs... args )
    {
        return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... );
    }
};


template<>
struct apply_wrap<0>
{
    template<typename R, typename... TupleArgs, typename... UnpackedArgs>
    static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>&, UnpackedArgs... args )
    {
        return f( args... );
    }
};



template<typename R, typename... TupleArgs>
R applyTuple( std::function<R(TupleArgs...)>& f, std::tuple<TupleArgs...> const& t )
{
    return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t );
}



int fac(int n)
{
    int r=1;
    for(int i=2; i<=n; ++i)
        r *= i;
    return r;
}



int main()
{
    auto t = std::make_tuple(5);
    auto f = std::function<decltype(fac)>(&fac);
    cout << applyTuple(f, t);
}

The news does not look good.这个消息看起来不太好。

Having read over the just-released draft standard , I'm not seeing a built-in solution to this, which does seem odd.阅读了刚刚发布的标准草案后,我没有看到对此的内置解决方案,这似乎很奇怪。

The best place to ask about such things (if you haven't already) is comp.lang.c++.moderated, because some folks involved in drafting the standard post there regularly.询问此类事情的最佳地点(如果您还没有)是 comp.lang.c++.moderated,因为有些人定期参与起草标准帖子。

If you check out this thread , someone has the same question (maybe it's you, in which case you're going to find this whole answer a little frustrating!), and a few butt-ugly implementations are suggested.如果您查看此线程,就会有人提出同样的问题(也许是您,在这种情况下,您会发现整个答案有点令人沮丧!),并且建议了一些丑陋的实现。

I just wondered if it would be simpler to make the function accept a tuple , as the conversion that way is easier.我只是想知道让函数接受tuple是否更简单,因为这种转换更容易。 But this implies that all functions should accept tuples as arguments, for maximum flexibility, and so that just demonstrates the strangeness of not providing a built-in expansion of tuple to function argument pack.但这意味着所有函数都应该接受元组作为参数,以获得最大的灵活性,因此这只是展示了不提供元组到函数参数包的内置扩展的奇怪之处。

Update: the link above doesn't work - try pasting this:更新:上面的链接不起作用 - 尝试粘贴:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661 http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661

How about this:这个怎么样:

// Warning: NOT tested!
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

using std::declval;
using std::forward;
using std::get;
using std::integral_constant;
using std::size_t;
using std::tuple;

namespace detail
{
    template < typename Func, typename ...T, typename ...Args >
    auto  explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    { return forward<Func>( f )( forward<Args>(a)... ); }

    template < size_t Index, typename Func, typename ...T, typename ...Args >
    auto  explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    {
        return explode_tuple( integral_constant<size_t, Index - 1u>{}, t,
         forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... );
    }
}

template < typename Func, typename ...T >
auto  run_tuple( Func &&f, tuple<T...> const &t )
 -> decltype( forward<Func>(f)(declval<T const>()...) )
{
    return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t,
     forward<Func>(f) );
}

template < typename Tret, typename ...T >
Tret  func_T( tuple<T...> const &t )
{ return run_tuple( &func<Tret, T...>, t ); }

The run_tuple function template takes the given tuple and pass its elements individually to the given function. run_tuple函数模板采用给定的元组并将其元素单独传递给给定的函数。 It carries out its work by recursively calling its helper function templates explode_tuple .它通过递归调用其辅助函数模板explode_tuple执行其工作。 It's important that run_tuple passes the tuple's size to explode_tuple ; run_tuple将元组的大小传递给explode_tuple很重要; that number acts as a counter for how many elements to extract.该数字充当要提取多少元素的计数器。

If the tuple is empty, then run_tuple calls the first version of explode_tuple with the remote function as the only other argument.如果元组为空,则run_tuple调用第一个版本的explode_tuple并将远程函数作为唯一的其他参数。 The remote function is called with no arguments and we're done.不带参数调用远程函数,我们就完成了。 If the tuple is not empty, a higher number is passed to the second version of explode_tuple , along with the remote function.如果元组不为空,则将更大的数字与远程函数一起传递给第二个版本的explode_tuple A recursive call to explode_tuple is made, with the same arguments, except the counter number is decreased by one and (a reference to) the last tuple element is tacked on as an argument after the remote function.explode_tuple进行递归调用,使用相同的参数,除了计数器编号减一并且(对)最后一个元组元素的引用作为远程函数之后的参数附加。 In a recursive call, either the counter isn't zero, and another call is made with the counter decreased again and the next-unreferenced element is inserted in the argument list after the remote function but before the other inserted arguments, or the counter reaches zero and the remote function is called with all the arguments accumulated after it.在递归调用中,计数器不为零,再次调用时计数器再次减少,并且下一个未引用的元素在远程函数之后但在其他插入的参数之前插入到参数列表中,或者计数器到达零,远程函数被调用,所有参数都在它之后累积。

I'm not sure I have the syntax of forcing a particular version of a function template right.我不确定我是否具有强制特定版本的函数模板正确的语法。 I think you can use a pointer-to-function as a function object;我认为您可以使用指向函数的指针作为函数对象; the compiler will automatically fix it.编译器会自动修复它。

I am evaluating MSVS 2013RC, and it failed to compile some of the previous solutions proposed here in some cases.我正在评估 MSVS 2013RC,在某些情况下,它无法编译此处提出的一些先前的解决方案。 For example, MSVS will fail to compile "auto" returns if there are too many function parameters, because of a namespace imbrication limit (I sent that info to Microsoft to have it corrected).例如,如果函数参数太多,MSVS 将无法编译“自动”返回,因为命名空间限制(我将该信息发送给 Microsoft 以进行更正)。 In other cases, we need access to the function's return, although that can also be done with a lamda: the following two examples give the same result..在其他情况下,我们需要访问函数的返回值,尽管这也可以使用 lamda 来完成:以下两个示例给出了相同的结果。

apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2));
ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));

And thanks again to those who posted answers here before me, I wouldn't have gotten to this without it... so here it is:再次感谢那些在我之前发布答案的人,没有它我就不会得到这个......所以它是:

template<size_t N>
struct apply_impl {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};

// This is a work-around for MSVS 2013RC that is required in some cases
#if _MSC_VER <= 1800 /* update this when bug is corrected */
template<>
struct apply_impl<6> {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&& t, A&&... a)
    -> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};
#endif

template<>
struct apply_impl<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&&, A&&... a)
    -> decltype(std::forward<F>(f)(std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::forward<A>(a)...);
    }
};

// Apply tuple parameters on a non-member or static-member function by perfect forwarding
template<typename F, typename T>
inline auto apply_tuple(F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
}

// Apply tuple parameters on a member function
template<typename C, typename F, typename T>
inline auto apply_tuple(C*const o, F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t));
}

Extending on @David's solution, you can write a recursive template that扩展@David 的解决方案,您可以编写一个递归模板

  1. Doesn't use the (overly-verbose, imo) integer_sequence semantics不使用 (overly-verbose, imo) integer_sequence语义
  2. Doesn't use an extra temporary template parameter int N to count recursive iterations不使用额外的临时模板参数int N来计算递归迭代
  3. (Optional for static/global functors) uses the functor as a template parameter for compile-time optimizaion (静态/全局函子可选)使用函子作为编译时优化的模板参数

Eg:例如:

template <class F, F func>
struct static_functor {
    template <class... T, class... Args_tmp>
    static inline auto apply(const std::tuple<T...>& t, Args_tmp... args)
            -> decltype(func(std::declval<T>()...)) {
        return static_functor<F,func>::apply(t, args...,
                std::get<sizeof...(Args_tmp)>(t));
    }
    template <class... T>
    static inline auto apply(const std::tuple<T...>& t, T... args)
            -> decltype(func(args...)) {
        return func(args...);
    }
};

static_functor<decltype(&myFunc), &myFunc>::apply(my_tuple);

Alternatively if your functor is not defined at compile-time (eg, a non- constexpr functor instance, or a lambda expression), you can use it as a function parameter instead of a class template parameter, and in fact remove the containing class entirely:或者,如果您的函子未在编译时定义(例如,非constexpr函子实例或 lambda 表达式),您可以将其用作函数参数而不是类模板参数,实际上完全删除包含类:

template <class F, class... T, class... Args_tmp>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
        Args_tmp... args) -> decltype(func(std::declval<T>()...)) {
    return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t));
}
template <class F, class... T>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
        T... args) -> decltype(func(args...)) {
    return func(args...);
}

apply_functor(&myFunc, my_tuple);

For pointer-to-member-function callables, you can adjust either of the above code pieces similarly as in @David's answer.对于指向成员函数的可调用指针,您可以像@David 的回答一样调整上述任一代码段。

Explanation解释

In reference to the second piece of code, there are two template functions: the first one takes the functor func , the tuple t with types T... , and a parameter pack args of types Args_tmp... .参考第二段代码,有两个模板函数:第一个接受函子func 、类型为T...的元组t和类型为Args_tmp...的参数包args When called, it recursively adds the objects from t to the parameter pack one at a time, from beginning ( 0 ) to end, and calls the function again with the new incremented parameter pack.调用时,它从头 ( 0 ) 到结尾递归地将t的对象一次添加到参数包中,并使用新增加的参数包再次调用该函数。

The second function's signature is almost identical to the first, except that it uses type T... for the parameter pack args .第二个函数的签名几乎与第一个相同,除了它使用类型T...作为参数包args Thus, once args in the first function is completely filled with the values from t , it's type will be T... (in psuedo-code, typeid(T...) == typeid(Args_tmp...) ), and thus the compiler will instead call the second overloaded function, which in turn calls func(args...) .因此,一旦第一个函数中的argst的值完全填充,它的类型将是T... (在伪代码中, typeid(T...) == typeid(Args_tmp...) ),并且因此编译器将改为调用第二个重载函数,后者又调用func(args...)

The code in the static functor example works identically, with the functor instead used as a class template argument.静态函子示例中的代码的工作方式相同,只是将函子用作类模板参数。

Why not just wrap your variadic arguments into a tuple class and then use compile time recursion (see link ) to retrieve the index you are interested in. I find that unpacking variadic templates into a container or collection may not be type safe wrt heterogeneous types为什么不将您的可变参数包装到一个元组类中,然后使用编译时递归(请参阅链接)来检索您感兴趣的索引。我发现将可变参数模板解包到容器或集合中可能不是类型安全的异构类型

template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...> 
{
    return std::make_tuple(args);
}

This simple solution works for me:这个简单的解决方案对我有用:

template<typename... T>
void unwrap_tuple(std::tuple<T...>* tp)
{
    std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl;
}

int main()
{
    using TupleType = std::tuple<int, float, std::string, void*>;

    unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction
}

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

相关问题 如何使用可变参数模板打印出函数的参数? - How do I print out the arguments of a function using a variadic template? 如何在0可变参数上专门化可变参数模板类? - How do I specialise a variadic template class on 0 variadic arguments? 是否可以在可变参数模板函数中扩展非可变参数? - Is it possible to expand non-variadic arguments in a variadic template function? 将元组的内容作为可变参数传递 - Pass tuple's content as variadic function arguments 将元组 TYPE 扩展为可变参数模板? - Expand a tuple TYPE into a variadic template? 模板函数将元组元素的可变参数数量绑定到另一个函数作为参数 - Template function binding variadic number of tuple's elements to another function as arguments 如何从可变参数模板参数创建 std::tuple&lt;&gt;? - How do I create an std::tuple<> from a variadic template parameter? 如何将元组 &lt;&gt; 剥离回可变参数模板类型列表? - How do I strip a tuple<> back into a variadic template list of types? 如何通过使用可变参数模板参数来专门化元组的类模板? - How to specialize a class template for a tuple by using variadic template arguments? 如果不支持可变参数模板参数,如何为元组专门化类模板? - How to specialize a class template for a tuple when variadic template arguments are not supported?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM