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

127 c++ arguments tuples c++11

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

template<typename Tret, typename... T> Tret func(const T&... t);
Run Code Online (Sandbox Code Playgroud)

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

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

有没有人有解决方案,或提示在哪里阅读它?

Dav*_*vid 45

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

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

提供了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 )
  {

  }
}
Run Code Online (Sandbox Code Playgroud)

  • 在所讨论的"函数"实际上是构造函数的情况下,是否可以使其适应? (2认同)

sig*_*agi 35

在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...> 
{}; 
Run Code Online (Sandbox Code Playgroud)

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

 // ----------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));
}
Run Code Online (Sandbox Code Playgroud)

测试结果如下:

// --------------------- 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;
}
Run Code Online (Sandbox Code Playgroud)

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


DRa*_*ayX 31

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

#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));
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

void foo(int i, bool b);

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

void m()
{
    apply(&foo, t);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,GCC(至少4.6)无法编译"抱歉,未实现:重载过载"(这只是意味着编译器还没有完全实现C++ 11规范),并且因为它使用了可变参数模板,所以它不会在MSVC工作,所以它或多或少都没用.但是,一旦有一个支持规范的编译器,它将是最好的方法恕我直言.(注意:修改它并不难,以便您可以解决GCC中的缺陷,或者使用Boost预处理器来实现它,但它会破坏优雅,所以这就是我发布的版本.)

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));
}
Run Code Online (Sandbox Code Playgroud)

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

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));
}
Run Code Online (Sandbox Code Playgroud)
// 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 在ideaone上的gcc版本是旧的,因为它传递,它不支持受损的decltype返回类型重载.我在gcc 4.7.2中相对彻底地测试了这段代码,并没有遇到任何问题.使用gcc 4.8,您可以使用新的C++ 17自动返回值功能来避免所有令人讨厌的decltype尾随返回类型. (3认同)
  • 出于好奇,我尝试在GCC 4.8中编译它,并且`foo('x',true)`编译成与`apply(foo,:: std :: make_tuple('x',true)完全相同的汇编代码) `除了-O0之外还有任何优化级别. (3认同)
  • 使用C++ 14`integer_sequence`,您甚至可以在其示例中获得几乎正确的`apply()`实现.请参阅下面的答案. (2认同)

Pet*_*Som 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());
}
Run Code Online (Sandbox Code Playgroud)

这是使用index_sequence改编自C++ 14草案.我可能会建议申请未来的标准(TS).


M. *_*gan 28

在C++ 17中,您可以这样做:

std::apply(the_function, the_tuple);
Run Code Online (Sandbox Code Playgroud)

这已经在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;
}
Run Code Online (Sandbox Code Playgroud)

这种解决方案是对传递过载集和函数模板的一般问题的简化解决方案.这里给出了一般解决方案(一个负责完美转发,constexpr-ness和noexcept-ness的解决方案):https://blog.tartanllama.xyz/passing-overload-sets/ .

  • @Zitrax 您可以指定函数的模板参数:`std::apply(add_generic&lt;float&gt;, std::make_pair(2.0f, 3.0f));` (2认同)

Dan*_*ker 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

  • 因为 tuple&lt;int, char, string&gt; 作为单独的类型是必要的;能够创建一个在每次调用过程中不需要 make_type 的函数。 (2认同)