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


135

考虑带有可变参数模板参数的模板化函数的情况:

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

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

有人建议使用手写递归黑客技术,但是可变参数模板参数的真正价值在于能够在上述情况下使用它们。

有没有人对is有解决方案,或暗示在哪里可以找到它?


5
C ++ 14标准有一个解决方案。open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
Skeen,

1
想法是使用integer_sequence,在单个可变参数爆炸中将元组解包,请参见en.cppreference.com/w/cpp/utility/integer_sequence
Skeen

6
有了integer_sequence S,您只需将函数调用为func(std::get<S>(tuple)...),然后让编译器处理其余部分。
Skeen 2013年

1
如果使用C ++ 17或更高版本,请忽略此答案,并使用std :: apply查看下面的答案
lewis

Answers:


46

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

基本上在编译时,编译器将递归地展开各种包含函数调用中的所有参数<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 )
  {

  }
}

2
在所讨论的“函数”实际上是构造函数的情况下,是否可以使其适用?
HighCommander4'9

您能否提供一个您想做的事的例子,我们可以从那里做起。
David

该解决方案仅提供编译时间开销,最后将简化为(pObj-> * f)(arg0,arg,1,... argN); 对?
Goofy

是的,编译器会将多个函数调用压缩为最后一个,就像您自己编写的那样,这是所有这些元编程内容的美。
David

现在所有tr1东西都可以用c ++ 11删除
Ryan Haining

37

在C ++ 17中,您可以执行以下操作:

std::apply(the_function, the_tuple);

使用std :: experimental :: apply在Clang ++ 3.9中已经可以使用。

回应说如果将其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

1
@Zitrax您可以指定函数的模板参数:std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Erbureth说,恢复莫妮卡

这是最简单,最优雅的解决方案。它创造奇迹。非常感谢M. Alaggan !!!!!! +100票
艾略特

36

在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...) {}并尝试使用apply,它将无法编译。
Zitrax

32

我发现这是最优雅的解决方案(并且已被最佳转发):

#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中工作,因此它几乎没有用。但是,一旦有一个支持该规范的编译器,它将是IMHO的最佳方法。(注意:修改它并不难,这样您就可以解决GCC中的缺陷,或者使用Boost Preprocessor来实现它,但是却破坏了样式,因此这是我要发布的版本。)

现在,GCC 4.7支持此代码。

编辑:在实际函数调用周围添加了向前功能,以支持右值引用形式*如果您使用的是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
在列出的答案中+1,您的答案是我使用参数为向量的参数时最接近的... ...但是我仍然遇到编译错误。ideone.com/xH5kBH如果使用-DDIRECT_CALL进行编译并运行,则将看到输出结果。否则我会遇到编译错误(我认为decltype不够聪明,无法找出我的特殊情况),使用gcc 4.7.2。
kfmfe04

3
ideaone上的gcc版本太旧,无法通过,它不支持损坏的decltype返回类型重载。我已经在gcc 4.7.2中相对彻底地测试了此代码,并且没有遇到任何问题。使用gcc 4.8,您可以使用新的C ++ 17自动返回值功能来避免所有讨厌的decltype尾随返回类型。
DRayX 2012年

1
出于好奇,在非成员apply函数中,为什么f不像std::forward返回类型那样用调用包装?不需要吗?
Brett Rossier 2013年

3
出于好奇,我尝试在GCC 4.8中进行foo('x', true)编译,然后编译为apply(foo, ::std::make_tuple('x', true))与-O0以外的任何优化级别完全相同的汇编代码。
DRayX

2
使用C ++ 14,integer_sequence您甚至可以apply()在其示例中获得几乎正确的实现。请参阅下面的答案。
PeterSom

28
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_sequence改编而成的。我可能会建议在将来的标准(TS)中应用。


1

这个消息看起来不好。

阅读了刚刚发布的标准草案,我没有看到内置的解决方案,这似乎很奇怪。

关于此类问题(如果您还没有的话)的最好地方是comp.lang.c ++。moderated,因为有些人定期参与那里起草标准帖子。

如果您签出此线程,则有人会有相同的问题(也许是您,在这种情况下,您会发现整个答案有些令人沮丧!),并建议了一些非常丑陋的实现。

我只是想知道使函数接受a会更简单tuple,因为这种方式的转换更容易。但这意味着所有函数都应接受元组作为参数,以实现最大的灵活性,并且这仅表明了不提供元组到函数参数包的内置扩展的奇怪之处。

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

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


我不知道为什么他们甚至还要为元组和函数参数包分别设置一个概念。也许在兼容的编译器中它们是可以互换的,但是我没有发现任何有关它们的资料。
Daniel Earwicker 09年

2
因为tuple <int,char,string>是单独的类型是必需的;以及在每个调用过程中创建不需要make_type的函数的能力。
coppro

1
另外,最好的地方不是comp.lang.c ++。moderated。有关C ++ 1x的问题几乎总是更好地针对comp.std.c ++。
coppro

1

所有这些实现都是好的。但是由于使用指向成员函数的指针,编译器通常无法内联目标函数调用(至少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

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

0

这个怎么样:

// 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则使用相同的参数,不同之处在于计数器的数量减少一个,并且最后一个元组元素(作为对它的引用)在之后附加为参数远程功能。在递归调用中,要么计数器不为零,然后进行另一个调用,使计数器再次减小,然后将下一个未引用的元素插入到远程函数之后但在其他插入的参数之前的参数列表中,否则计数器到达零,远程函数全部调用 在那之后积累的论点。

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


0

我正在评估MSVS 2013RC,在某些情况下,它无法编译此处提出的某些先前解决方案。例如,由于命名空间限制(我已将该信息发送给Microsoft进行更正),如果功能参数太多,MSVS将无法编译“自动”返回。在其他情况下,我们也需要访问函数的返回值,尽管这也可以使用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呢?
tower120 2014年

0

扩展@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的类型Args_tmp...。调用时,它将t从开始(0)到结束一次将对象从中递归地添加到参数包中,然后使用新的递增参数包再次调用该函数。

第二个函数的签名几乎与第一个函数相同,除了它使用type T...作为参数pack args。因此,一旦args第一个函数完全用from中的值填充t,它的类型将是T...(在psuedo代码中typeid(T...) == typeid(Args_tmp...)),因此编译器将改为调用第二个重载函数,后者又调用func(args...)

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


对于第一个选项的编译时优化的任何评论将不胜感激,因此我可以使答案更完整(也许还可以学到一些新知识)。
CrepeGoat

-3

为什么不只是将可变参变量包装到元组类中,然后使用编译时递归(请参阅链接)来检索您感兴趣的索引。我发现将可变参模板解压缩到容器或集合中可能不是类型安全的wrt异构类型

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

6
问题是相反的。不是Args...-> tuple,而是tuple-> Args...
Xeo 2012年

-4

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

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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.