タプルを可変長テンプレート関数の引数に拡張するにはどうすればよいですか?


135

可変個のテンプレート引数を持つテンプレート関数の場合を考えます。

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

今、私はt値のタプルを持っています。func()引数としてタプル値を使用して呼び出すにはどうすればよいですか?bind()functionオブジェクトとcall()functionについて、そしてapply()いくつかの古くなったドキュメントで関数についても読みました。GNU GCC 4.4実装call()bind()クラスに関数があるようですが、この件に関するドキュメントはほとんどありません。

一部の人々は手書きの再帰的ハックを提案しますが、可変テンプレートの引数の真の価値は、上記のような場合にそれらを使用できることです。

誰かが解決策を持っているか、それについてどこで読むべきかについてのヒントはありますか?


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)...)、残りをコンパイラに処理させるだけです。
2013年

1
C ++ 17以降を使用している場合は、この回答を無視して、std :: apply
lewis

回答:


46

誰かが興味があればここに私のコードです

基本的にコンパイル時に、コンパイラーはさまざまな包括的関数呼び出しのすべての引数を再帰的に展開します<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 )
  {

  }
}

2
問題の「関数」が実際にコンストラクターである場合に機能するようにこれを適合させることは可能ですか?
HighCommander4

やりたいことの例を教えていただければ、そこから進むことができます。
David

このソリューションはコンパイル時間のオーバーヘッドのみを提供し、最終的には(pObj-> * f)(arg0、arg、1、... argN)に簡略化されます。正しい?
Goofy

はい、コンパイラは複数の関数呼び出しを最終的なものに圧縮します。まるで自分で書いたかのように、これがすべてのメタプログラミングの美しさです。
David

すべてのtr1ものは今c ++ 11で
取り出せる

37

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

この回避策は、関数が期待されるオーバーロードセットと関数テンプレートを渡すという一般的な問題に対する単純化されたソリューションです。一般的なソリューション(perfect-forwarding、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 ++ではできますが、それほど複雑ではないと思います...


"...そしてそれらのタプル要素を可変個のテンプレート関数に適用します"。ただし、テストセクションには、テンプレート以外の可変関数のみが含まれます。likeを追加template<class ... T> void three(T...) {}して、applyを使用しようとすると、コンパイルされません。
Zitrax 2017

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はこのコードを問題なくサポートします。

編集:rvalue参照フォームをサポートするために、実際の関数呼び出しの前後に追加されました*これは、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でコンパイルして実行すると、出力がどうなるかがわかります。そうでない場合、gcc 4.7.2でコンパイルエラーが発生します(decltypeは私の特別なケースを理解するのに十分スマートではないと思います)。
kfmfe04

3
ideaoneのgccのバージョンは、これが通過するために古くなっています。壊れたdecltype戻り型のオーバーロードをサポートしていません。このコードをgcc 4.7.2で比較的徹底的にテストしましたが、問題は発生していません。gcc 4.8では、新しいC ++ 17自動戻り値機能を使用して、厄介なdecltypeの末尾の戻り型をすべて回避できます。
DRayX

1
好奇心から、非メンバーapply関数で、戻り値型のように、なぜ呼び出しでfラップされないのstd::forwardですか?必要ありませんか?
ブレット・ロッシエ、2013

3
好奇心から、これをGCC 4.8でfoo('x', true)コンパイルして、apply(foo, ::std::make_tuple('x', true))-O0以外のあらゆるレベルの最適化とまったく同じアセンブリコードにコンパイルしました。
DRayX 2013

2
C ++ 14 integer_sequenceを使用するとapply()、その例ではほぼ正しい実装も得られます。以下の私の答えを参照してください。
PeterSom 2013

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

これは、index_sequenceを使用したC ++ 14ドラフトから改作されたものです。私は将来の標準(TS)に適用することを提案するかもしれません。


1

ニュースは良くないようです。

たった今リリースされたドラフト規格を読んだだけで、これに対する組み込みのソリューションは見当たりません。

そのようなことについて(もしまだなら)質問するのに最適な場所はcomp.lang.c ++。moderatedです。

このスレッドをチェックすると、誰かが同じ質問をしているかもしれません(多分それはあなたです。この場合、この全体の答えが少しイライラします!)、お尻が醜い実装がいくつか提案されています。

tupleそのように変換する方が簡単なので、関数がを受け入れるようにする方が簡単かどうか疑問に思いました。しかしこれは、すべての関数がタプルを引数として受け入れる必要があることを意味し、柔軟性を最大化するため、タプルの組み込み引数拡張を関数引数パックに提供しないという奇妙さを示しています。

更新:上記のリンクは機能しません-これを貼り付けてみてください:

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


彼らがタプルと関数引数パックの別々の概念を持っていることさえ気にするのはなぜだろうと思います。多分、適合コンパイラでは互換性がありますが、私がそれらについて読んだどこにでもその兆候を見つけたことはありません。
Daniel Earwicker 2009年

2
tuple <int、char、string>は別の型として必要なので、すべての呼び出しの途中でmake_typeを必要としない関数を作成する機能も同様です。
coppro 2009

1
また、最適な場所はcomp.lang.c ++。moderatedではありません。C ++ 1xに関する質問は、ほとんど常にcomp.std.c ++に向けられています。
coppro 2009

1

この実装はすべて良好です。しかし、メンバー関数へのポインターを使用するため、コンパイラーはターゲット関数呼び出しをインライン化できないことがよくあります(少なくとも、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、リモート関数とともに、より大きな数値がの2番目のバージョンに渡されます。への再帰呼び出しexplode_tupleカウンター番号が1つ減り、最後のタプル要素(への参照)がリモート関数の後に引数として追加されることを除いて、同じ引数で作成されます。再帰呼び出しでは、カウンターがゼロではなく、カウンターが再度減少して別の呼び出しが行われ、次の非参照要素がリモート関数の後、他の挿入された引数の前に引数リストに挿入されるか、カウンターが到達しますゼロであり、その後に累積されたすべての引数を使用してリモート関数が呼び出されます

関数テンプレートの特定のバージョンを強制する構文があるかどうかはわかりません。関数へのポインタを関数オブジェクトとして使用できると思います。コンパイラーが自動的に修正します。


0

私はMSVS 2013RCを評価していますが、ここで提案されている以前のソリューションのいくつかをコンパイルできない場合があります。たとえば、名前空間の埋め込み制限のため、関数パラメーターが多すぎる場合、MSVSは「自動」リターンのコンパイルに失敗します(その情報をMicrosoftに送信して修正してもらいました)。他の場合では、関数の戻り値にアクセスする必要がありますが、ラムダを使用して実行することもできます。次の2つの例では同じ結果が得られます。

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 14

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ファンクターインスタンス、またはラムダ式など)、それをクラステンプレートパラメーターの代わりに関数パラメーターとして使用し、実際には包含クラスを完全に削除できます。

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の回答と同様に、上記のコードのいずれかを調整できます。

説明

2番目のコードを参照すると、2つのテンプレート関数があります。最初の関数は、ファンクターfunc、型付きのタプル、およびtT...のパラメーターパックargsを受け取りますArgs_tmp...。呼び出されると、t最初から(0)から最後まで、オブジェクトをパラメーターパックに1つずつ再帰的に追加し、新しいインクリメントされたパラメーターパックで関数を再度呼び出します。

2番目の関数のシグニチャーは、最初の関数とほとんど同じですがT...、パラメーターパックにタイプを使用する点が異なりますargs。したがって、args最初の関数がからの値で完全に満たされると、そのt型はT...(psuedo-codeでtypeid(T...) == typeid(Args_tmp...))になるため、コンパイラーは代わりに2番目のオーバーロードされた関数を呼び出し、次にを呼び出しますfunc(args...)

静的ファンクターの例のコードは同じように機能し、ファンクターがクラステンプレート引数として使用されます。


最初のオプションのコンパイル時の最適化についてのコメントをいただければ幸いです。そのため、回答をより完全なものにすることができます(そして、何か新しいことを学ぶことができます)。
CrepeGoat 2017年

-3

可変引数をタプルクラスにラップしてから、コンパイル時の再帰(リンクを参照)を使用して、目的のインデックスを取得しないのはなぜですか。可変テンプレートをコンテナーまたはコレクションにアンパックすることは、異種の型に対してタイプセーフではない可能性があります

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

6
問題はその逆でした。ないArgs...- > tupleが、tuple- > Args...
Xeo

-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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.