如何将元组扩展为可变参数模板函数的参数?
Posted
技术标签:
【中文标题】如何将元组扩展为可变参数模板函数的参数?【英文标题】:How do I expand a tuple into variadic template function's arguments? 【发布时间】:2010-10-15 19:23:27 【问题描述】:考虑带有可变模板参数的模板化函数的情况:
template<typename Tret, typename... T> Tret func(const T&... t);
现在,我有一个元组 t
的值。如何使用元组值作为参数调用func()
?
我已经阅读了 bind()
函数对象、call()
函数以及不同的一些现已过时的文档中的 apply()
函数。 GNU GCC 4.4 实现似乎在bind()
类中有一个call()
函数,但是关于这个主题的文档很少。
有些人建议使用手写递归技巧,但可变参数模板参数的真正价值在于能够在上述情况下使用它们。
有没有人有解决方案,或者提示在哪里阅读它?
【问题讨论】:
C++14标准有解决办法见; open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html 这个想法是使用integer_sequence
将元组解包到一个可变参数爆炸中,参见en.cppreference.com/w/cpp/utility/integer_sequence
拥有一个integer_sequence S
,您只需将您的函数称为func(std::get<S>(tuple)...)
,然后让编译器处理其余部分。
如果使用 C++17 或更高版本,请忽略此答案并使用 std::apply 查看下面的答案
【参考方案1】:
在 C++17 中,您可以这样做:
std::apply(the_function, the_tuple);
这已经在 Clang++ 3.9 中工作,使用 std::experimental::apply。
回复评论说如果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;
这个变通方法是对在期望函数的地方传递重载集和函数模板的一般问题的简化解决方案。此处介绍了通用解决方案(一种处理完美转发、constexpr-ness 和 noexcept-ness):https://blog.tartanllama.xyz/passing-overload-sets/。
【讨论】:
根据std::apply 的示例代码,如果the_function
被模板化,它似乎不起作用。
@Zitrax 您可以指定函数的模板参数:std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
这是最简单、最优雅的解决方案。它创造了奇迹。非常感谢,阿拉根先生!!!!!! +100 票【参考方案2】:
如果有人感兴趣,这是我的代码
基本上在编译时,编译器将递归展开各种包含函数调用中的所有参数
提供了 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 )
【讨论】:
如果有问题的“函数”实际上是一个构造函数,是否可以调整它来工作? 你能提供一个你想做的例子,我们可以从那里开始。 此解决方案仅提供编译时开销,最后将简化为 (pObj->*f)(arg0, arg,1, ... argN);对吗? 是的,编译器会将多个函数调用压缩到最后一个,就好像你自己编写的一样,这就是所有这些元编程东西的美妙之处。 所有tr1
的东西现在都可以用c++11取出了【参考方案3】:
在 C++ 中,有很多方法可以扩展/解包元组并将这些元组元素应用于可变参数模板函数。这是一个创建索引数组的小助手类。它在模板元编程中被大量使用:
// ------------- 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++ 可以,而且我认为它并没有那么复杂......
【讨论】:
"... 并将这些元组元素应用于可变参数模板函数"。测试部分只包含非模板可变参数函数。如果我添加一个像template<class ... T> void three(T...)
并尝试使用它不会编译的应用。
不幸的是,这种技术不适用于 nvcc(可能还有其他基于 EDG 的编译器)。它以error: template parameter pack not at end of parameter list
失败,在template<class Ret, class... Args, int... Indexes > Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
行中。【参考方案4】:
我发现这是最优雅的解决方案(并且是最佳转发):
#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 Preprocessor 实现它并不难,但它破坏了优雅,所以这是我发布的版本。)
GCC 4.7 现在支持这个代码就好了。
编辑:在实际函数调用周围添加前向以支持右值引用形式 *this,以防您使用 clang(或者如果其他人实际上可以添加它)。
编辑:在非成员应用函数主体中的函数对象周围添加了缺少的前向。感谢 pheedbaq 指出它丢失了。
编辑:这里是 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);
【讨论】:
+1 在列出的答案中,您的答案是我最接近使用参数为向量的参数... ...但我仍然遇到编译错误。 ideone.com/xH5kBH 如果你用 -DDIRECT_CALL 编译它并运行它,你会看到输出应该是什么。否则我会收到编译错误(我认为 decltype 不够聪明,无法弄清楚我的特殊情况),使用 gcc 4.7.2。 ideaone 上的 gcc 版本太旧了,它不支持损坏的 decltype 返回类型重载。我在 gcc 4.7.2 中对这段代码进行了比较彻底的测试,没有遇到任何问题。使用 gcc 4.8,您可以使用新的 C++17 自动返回值功能来避免所有讨厌的 decltype 尾随返回类型。 出于好奇,在非成员apply
函数中,为什么f
没有被std::forward
调用包裹,因为它在返回类型中?不需要吗?
出于好奇,我尝试在 GCC 4.8 中编译它,并将 foo('x', true)
编译为与 apply(foo, ::std::make_tuple('x', true))
完全相同的汇编代码,除了 -O0 之外还进行了任何级别的优化。
使用 C++14 integer_sequence
,您甚至可以在其示例中获得几乎正确的 apply()
实现。请参阅下面的答案。【参考方案5】:
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());
这是使用 index_sequence 改编自 C++14 草案。我可能会建议在未来的标准 (TS) 中应用。
【讨论】:
【参考方案6】:所有这些实现都很好。但是由于使用指向成员函数的指针编译器经常不能内联目标函数调用(至少gcc 4.8不能,不管Why gcc can't inline function pointers that can be determined?)
但是如果将指向成员函数的指针作为模板参数而不是作为函数参数发送,情况就会发生变化:
/// from https://***.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....)
此外,这是唯一一种适用于模板化函数的解决方案。
【讨论】:
【参考方案7】: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) 如果您没有现成的 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);
【讨论】:
【参考方案8】:这个消息看起来不太好。
阅读了just-released draft standard,我没有看到一个内置的解决方案,这看起来很奇怪。
询问此类问题的最佳位置(如果您还没有的话)是 comp.lang.c++.moderated,因为那里有一些人定期参与起草标准帖子。
如果你查看this thread,有人有同样的问题(也许是你,在这种情况下你会发现整个答案有点令人沮丧!),并建议了一些丑陋的实现。
我只是想知道让函数接受tuple
是否会更简单,因为这种方式的转换更容易。但这意味着所有函数都应该接受元组作为参数,以获得最大的灵活性,这只是说明了不提供元组的内置扩展到函数参数包的奇怪之处。
更新:上面的链接不起作用 - 尝试粘贴:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
【讨论】:
我想知道他们为什么还要为元组和函数参数包的不同概念而烦恼。也许在符合标准的编译器中它们是可以互换的,但我在读过它们的任何地方都没有发现任何迹象。 因为 tuple这个怎么样:
// 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
进行递归调用,除了计数器编号减一并且(对)最后一个元组元素的引用作为远程函数之后的参数。在递归调用中,计数器不为零,并且在计数器再次减小的情况下进行另一个调用,并且下一个未引用的元素被插入到远程函数之后但在其他插入的参数之前的参数列表中,或者计数器达到零,远程函数调用 all 在它之后累积的参数。
我不确定我是否有强制使用特定版本的函数模板的语法。我认为您可以将指向函数的指针用作函数对象;编译器会自动修复它。
【讨论】:
【参考方案10】:我正在评估 MSVS 2013RC,但在某些情况下,它未能编译此处提出的一些以前的解决方案。例如,如果函数参数过多,MSVS 将无法编译“自动”返回,因为命名空间重叠限制(我将该信息发送给 Microsoft 以进行更正)。在其他情况下,我们需要访问函数的返回值,尽管这也可以通过 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));
再次感谢那些在我之前在这里发布答案的人,如果没有它,我不会得到这个......所以它是:
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));
【讨论】:
为什么将对象参数设为 const 指针?不是引用,不是 const 引用,不仅仅是指针?如果可调用函数不会const
怎么办?【参考方案11】:
扩展@David 的解决方案,您可以编写一个递归模板
-
不使用(过于冗长,imo)
integer_sequence
语义
不使用额外的临时模板参数int N
来计算递归迭代次数
(对于静态/全局仿函数可选)将仿函数用作编译时优化的模板参数
例如:
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
,类型为@987654330 @。调用时,它递归地将 t
中的对象从开始 (0
) 一次添加到参数包中,并使用新的递增参数包再次调用该函数。
第二个函数的签名几乎与第一个相同,只是它使用T...
类型作为参数包args
。因此,一旦第一个函数中的args
完全被来自t
的值填充,它的类型将是T...
(在伪代码中,typeid(T...) == typeid(Args_tmp...)
),因此编译器将改为调用第二个重载函数,然后调用func(args...)
。
静态函子示例中的代码工作方式相同,只是将函子用作类模板参数。
【讨论】:
任何关于第一个选项的编译时优化的 cmets 将不胜感激,因此我可以使我的答案更完整(并且可能学到一些新东西)。【参考方案12】:为什么不直接将可变参数包装到一个元组类中,然后使用编译时递归(参见link)来检索您感兴趣的索引。我发现将可变参数模板解包到容器或集合中可能不是类型安全的异构类型
template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...>
return std::make_tuple(args);
【讨论】:
问题正好相反。不是Args...
-> tuple
,而是tuple
-> Args...
。【参考方案13】:
这个简单的解决方案对我有用:
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
【讨论】:
以上是关于如何将元组扩展为可变参数模板函数的参数?的主要内容,如果未能解决你的问题,请参考以下文章