如何将 tuple 展开为可变参数模板函数的参数?

考虑一个带有可变参数模板参数的模板函数:

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

现在,我有一个值的元组 t。如何使用元组值作为参数调用 func()? 我已经阅读了 bind()函数对象和 call()函数,以及一些现已过时的不同文档中的 apply()函数。GNU GCC 4.4实现在 bind()类中似乎有一个 call()函数,但是关于这个主题的文档很少。

有些人建议使用手写的递归方法,但是可变参数模板参数的真正价值在于能够在上面这样的情况下使用它们。

是否有人有一个解决方案,或提示在哪里阅读?

69697 次浏览

这个消息看起来不妙。

在阅读了 刚刚发布的草案标准之后,我没有看到这个问题的内置解决方案,这看起来很奇怪。

询问此类问题的最佳地点(如果您还没有这样做过的话)是 comp.lang.c + + 。有节制,因为有些人参与起草标准的职位定期在那里。

如果你检查 这根线,有人有同样的问题(也许是你,在这种情况下,你会发现整个答案有点令人沮丧!),并提出了一些丑陋的实现。

我只是想知道让函数接受 tuple是否会更简单,因为这样的转换更容易。但是这意味着所有的函数都应该接受元组作为参数,以获得最大的灵活性,因此这恰恰说明了不提供内置的元组到函数参数包的扩展的奇怪之处。

更新: 上面的链接不起作用-试着粘贴这个:

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

如果有人感兴趣,这是我的代码

基本上,在编译时,编译器会递归地展开各种包含函数调用中的所有参数 < N >-> 调用 < N-1 >-> 调用...-> 调用 < 0 > ,这是最后一个,编译器会优化掉各种中间函数调用,只保留最后一个相当于 func 的函数调用(arg1,arg2,arg3,...)

提供了两个版本,一个用于对象调用的函数,另一个用于静态函数。

#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 )
{


}
}

在 C + + 中,有很多方法可以展开/解压 tuple,并将这些 tuple 元素应用到一个可变参数模板函数中。下面是一个创建索引数组的小型助手类。它在模板超编程中被广泛使用:

// ------------- 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...>
{};

现在执行这项工作的代码并不是那么大:

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

我不是其他语言方面的专家,但我想如果这些语言的菜单中没有这样的功能,就没有办法做到这一点。至少用 C + + 你可以,而且我认为它没有那么复杂..。

为什么不将可变参数包装到一个元组类中,然后使用编译时递归(参见 链接)来检索您感兴趣的索引。我发现将可变模板解包到容器或集合中可能不是类型安全的 W.R.T. 异构类型

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

我发现这是最优雅的解决方案(它被最佳地转发了) :

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

示例用法:

void foo(int i, bool b);


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


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

不幸的是,GCC (至少4.6)没有用“抱歉,没有实现: 超载”(这只是意味着编译器还没有完全实现 C + + 11规范)来编译它,而且因为它使用可变模板,它在 MSVC 中不能工作,所以它或多或少是无用的。然而,一旦有一个编译器支持该规范,它将是最好的方法。(注意: 修改这个版本并不难,这样你就可以解决 GCC 中的缺陷,或者用 Boost 预处理器实现它,但是它破坏了优雅,所以这是我发布的版本。)

GCC 4.7现在完全支持这种代码。

编辑: 为了支持 rvalue 引用表单,在实际函数调用前后添加了转发,以防您使用 clang (或者其他人实际上正在添加它)。

编辑: 在非成员应用程序函数体中的函数对象周围添加了前向缺失。谢谢 Peedbaq 指出它不见了。

编辑: 这是 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));
}

下面是成员函数的一个版本(没有经过很多测试!) :

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

这样吧:

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

run_tuple函数模板接受给定的元组,并将其元素分别传递给给定的函数。它通过递归调用辅助函数模板 explode_tuple来执行它的工作。重要的是,run_tuple将元组的大小传递给 explode_tuple; 该数字作为要提取的元素数量的计数器。

如果元组为空,那么 run_tuple将调用 explode_tuple的第一个版本,并将远程函数作为唯一的其他参数。远程函数在没有参数的情况下被调用,我们就完成了。如果元组不为空,则会向第二个版本的 explode_tuple以及远程函数传递一个更高的数字。使用相同的参数对 explode_tuple进行递归调用,除了计数器数量减少一个,并且(对)最后一个元组元素作为参数附加在远程函数之后。在递归调用中,要么计数器不是零,而另一个调用使计数器再次减少,并且下一个未被引用的元素被插入到参数列表中远程函数之后,但在其他插入的参数之前,要么计数器达到零,远程函数被 所有调用之后积累的参数。

我不确定是否具有强制函数模板的特定版本正确的语法。我认为可以使用指针函数作为函数对象; 编译器会自动修复它。

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

这是从 C + + 14草案中使用 index _ order 改编而来的,我可能会建议在将来的标准(TS)中应用。

我正在评估 MSVS 2013RC,在某些情况下,它未能编译此处提出的一些以前的解决方案。例如,如果有太多的函数参数,MSVS 将无法编译“ auto”返回,这是由于名称空间的复杂限制(我将这一信息发送给微软进行更正)。在其他情况下,我们需要访问函数的返回值,尽管这也可以通过 lada 实现: 下面两个例子给出了相同的结果。.

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

再次感谢那些在我之前在这里发布答案的人,没有它我不会得到这个... 所以这里是:

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

这个简单的解决方案对我很有效:

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
}

所有这些实现都很好。但由于使用指针指向成员函数,编译器往往不能内联目标函数调用(至少 gcc 4.8不能,无论什么 为什么 gcc 不能内联函数指针,可以确定?)

但是,如果将成员函数的指针作为模板参数而不是函数参数发送,情况就会发生变化:

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

用法:

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

不可连接 http://goo.gl/5UqVnC的证明


通过小的改变,我们可以“超载”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....)

另外,这是唯一一个可以使用模板函数的解决方案。

1)如果你有一个现成的参数 _ 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)如果你没有一个现成的 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);
}

在 C + + 17中你可以这样做:

std::apply(the_function, the_tuple);

这已经可以在 Clang + + 3.9中使用了,使用的是 std: : economic: : application。

有评论说,如果 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;
}

这项工作是一个简化的解决方案,通过重载集和函数模板的一般问题,其中一个函数将是预期的。这里给出了通用的解决方案(一种处理完全转发、常量和不可除性的解决方案) : https://blog.tartanllama.xyz/passing-overload-sets/

在@David 的解决方案的基础上,您可以编写一个递归模板

  1. 不使用(过于冗长,imo) integer_sequence语义
  2. 不使用额外的临时模板参数 int N来计算递归迭代次数
  3. (静态/全局函数可选)使用函数作为编译时优化的模板参数

例如:

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

或者,如果您的函数没有在编译时定义(例如,一个非 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);

对于指针到成员函数的调用,可以像@David 的回答那样调整上面的任何一段代码。

解释

关于第二段代码,有两个模板函数: 第一个模板函数采用函数 func、类型为 T...的元组 t和类型为 Args_tmp...的参数包 args。当被调用时,它会递归地将来自 t的对象一次一个地添加到参数包中,从开始(0)到结束,然后再次使用新的递增参数包调用函数。

第二个函数的签名与第一个函数几乎完全相同,只是它对参数包 args使用 T...类型。因此,一旦第一个函数中的 args完全用来自 t的值填充,它的类型将是 T...(在 psuedo-code 中为 typeid(T...) == typeid(Args_tmp...)) ,因此编译器将调用第二个重载函数,而后者又调用 func(args...)

静态函数示例中的代码工作原理相同,函数用作类模板参数。