156

可変個引数のテンプレート引数を持つテンプレート化された関数の場合を考えてみましょう:

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

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

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

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

4

13 に答える 13

58

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/ に示されています。

于 2016-05-08T13:59:50.387 に答える
47

誰かが興味を持っているなら、これが私のコードです

基本的に、コンパイル時に、コンパイラーはさまざまな包括的関数呼び出し<N>->呼び出し<N-1>->呼び出し...->最後の呼び出し<0>のすべての引数を再帰的に展開し、コンパイラーは最適化しますfunc(arg1、arg2、arg3、...)と同等の最後の関数のみを保持するためのさまざまな中間関数呼び出し

2つのバージョンが提供されています。1つはオブジェクトで呼び出される関数用で、もう1つは静的関数用です。

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

  }
}
于 2009-10-10T05:13:01.150 に答える
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++ では可能で、それほど複雑ではないと思います...

于 2011-06-23T12:45:32.263 に答える
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で動作するため、多かれ少なかれ役に立たない。ただし、仕様をサポートするコンパイラがあれば、それが私見の最善のアプローチになります。(注: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);
}
于 2012-09-29T05:04:55.517 に答える
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

于 2009-03-26T21:56:21.683 に答える
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_tuplerun_tupleタプルのサイズを に渡すことが重要explode_tupleです。その数は、抽出する要素の数のカウンターとして機能します。

タプルが空の場合、他の唯一の引数としてリモート関数を使用してrun_tupleの最初のバージョンを呼び出します。explode_tupleリモート関数は引数なしで呼び出され、完了です。explode_tupleタプルが空でない場合は、より大きな数値がリモート関数とともにの 2 番目のバージョンに渡されます。への再帰呼び出しexplode_tupleカウンター番号が 1 減らされ、最後のタプル要素 (への参照) がリモート関数の後に引数として追加されることを除いて、同じ引数で作成されます。再帰呼び出しでは、カウンターがゼロではなく、カウンターが再び減少して別の呼び出しが行われ、リモート関数の後、他の挿入された引数の前に、次の参照されていない要素が引数リストに挿入されるか、カウンターがゼロであり、その後に蓄積されたすべての引数を使用してリモート関数が呼び出されます。

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

于 2013-04-29T09:22:54.273 に答える
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 つのテンプレート関数があります。最初の関数は functor 、 types を持つfuncタプル、およびtypesのパラメーター パックを取ります。呼び出されると、最初 ( ) から最後まで一度に 1 つずつパラメーター パックにオブジェクトを再帰的に追加し、新しくインクリメントされたパラメーター パックを使用して関数を再度呼び出します。tT...argsArgs_tmp...t0

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

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

于 2017-10-31T19:33:01.420 に答える
-3

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

template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...> 
{
    return std::make_tuple(args);
}
于 2012-02-04T05:27:49.700 に答える
-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
}
于 2014-06-12T15:06:02.737 に答える