28

だから私はいくつかのタイプを持っていますX:

typedef ... X;

およびテンプレート関数f:

class <typename T>
void f(X& x_out, const T& arg_in);

そして関数g

void g(const X* x_array, size_t x_array_size);

これを行う可変個引数テンプレート関数hを作成する必要があります。

template<typename... Args>
void h(Args... args)
{
    constexpr size_t nargs = sizeof...(args); // get number of args
    X x_array[nargs]; // create X array of that size

    for (int i = 0; i < nargs; i++) // foreach arg
        f(x_array[i], args[i]); // call f (doesn't work)

    g(x_array, nargs); // call g with x_array
}

動作しない理由は、実行時にそのような引数を添え字付けできないためです。

の中間部分を置き換える最良の手法は何hですか?

そして勝者は Xeo です。

template<class T> X fv(const T& t) { X x; f(x,t); return x; }

template<class... Args>
void h(Args... args)
{
  X x_array[] = { fv(args)... };

  g(x_array, sizeof...(Args));
}

(実際、私の特定のケースでは、出力パラメーターとしてではなく値で x を返すように f を書き直すことができるため、上記の fv は必要ありません)

4

5 に答える 5

30

リファクタリングまたはラップfして、パスする代わりに新しいものを返すことができますX。これは、パックの拡張を手に入れ、関数を非常に簡潔にするためです。

template<class T>
X fw(T const& t){ X x; f(x, t); return x; }

template<class... Args>
void h(Args... args){
  X xs[] = { fw(args)... };
  g(xs, sizeof...(Args));
}

実例。

gそして、を受け入れるように変更できればstd::initializer_list、さらに簡潔になります。

template<class... Args>
void h(Args... args){
  g({f(args)...});
}

実例。または(おそらくより良い)、g実際に転送するラッパーだけを提供することもできますg

void g(X const*, unsigned){}

void g(std::initializer_list<X> const& xs){ g(xs.begin(), xs.size()); }

template<class... Args>
void h(Args... args){
  g({f(args)...});
}

実例。
編集:別のオプションは、一時配列を使用することです。

template<class T>
using Alias = T;

template<class T>
T& as_lvalue(T&& v){ return v; }

template<class... Args>
void h(Args... args){
  g(as_lvalue(Alias<X[]>{f(args)...}), sizeof...(Args));
}

実例。この関数は危険であることに注意してくださいas_lvalue。配列は完全な式(この場合)が終了するまでしか存続しないgため、使用するときは注意が必要です。言語の文法上、許可されていないため、これAliasが必要です。X[]{ ... }

それがすべて不可能な場合は、argsパックのすべての要素にアクセスするために再帰が必要になります。

#include <tuple>

template<unsigned> struct uint_{}; // compile-time integer for "iteration"

template<unsigned N, class Tuple>
void h_helper(X (&)[N], Tuple const&, uint_<N>){}

template<unsigned N, class Tuple, unsigned I = 0>
void h_helper(X (&xs)[N], Tuple const& args, uint_<I> = {}){
  f(xs[I], std::get<I>(args));
  h_helper(xs, args, uint_<I+1>());
}

template<typename... Args>
void h(Args... args)
{
    static constexpr unsigned nargs = sizeof...(Args);
    X xs[nargs];

    h_helper(xs, std::tie(args...));

    g(xs, nargs);
}

実例。

編集: ecatmurのコメントに触発されて、私はインデックストリックを使用して、パックの拡張だけで、変更せずにそのままで動作するようにfgました。

template<unsigned... Indices>
struct indices{
  using next = indices<Indices..., sizeof...(Indices)>;
};
template<unsigned N>
struct build_indices{
  using type = typename build_indices<N-1>::type::next;
};
template <>
struct build_indices<0>{
  using type = indices<>;
};
template<unsigned N>
using IndicesFor = typename build_indices<N>::type;

template<unsigned N, unsigned... Is, class... Args>
void f_them_all(X (&xs)[N], indices<Is...>, Args... args){
  int unused[] = {(f(xs[Is], args), 1)...};
  (void)unused;
}

template<class... Args>
void h(Args... args){
  static constexpr unsigned nargs = sizeof...(Args);
  X xs[nargs];
  f_them_all(xs, IndicesFor<nargs>(), args...);
  g(xs, nargs);
}

実例。

于 2012-08-19T23:03:46.590 に答える
7

質問の最初の部分の答えとしての素敵なテンプレート:

template <class F, class... Args> 
void for_each_argument(F f, Args&&... args) {
    [](...){}((f(std::forward<Args>(args)), 0)...);
}
于 2015-02-02T14:04:06.447 に答える
6

明らかです。反復ではなく再帰を使用します。可変個引数テンプレートを扱うときは、常に再帰的な何かが入ります。要素をstd::tuple<...>使用してバインドする場合でもtie()、再帰的です。再帰的なビジネスがタプルによって行われるのはたまたまです。あなたの場合、次のようなものが必要なようです(おそらくいくつかのタイプミスがありますが、全体的にはこれでうまくいくはずです):

template <int Index, int Size>
void h_aux(X (&)[Size]) {
}

template <int Index, int Size, typename Arg, typename... Args>
void h_aux(X (&xs)[Size], Arg arg, Args... args) {
    f(xs[Index], arg);
    h_aux<Index + 1, Size>(xs, args...);
}

template <typename... Args>
void h(Args... args)
{
    X xs[sizeof...(args)];
    h_aux<0, sizeof...(args)>(xs, args...);
    g(xs, sizeof...(args));
}

nargsを使用して配列のサイズを定義することもできないと思います。定数式であることをコンパイラに示すものはありません。

于 2012-08-19T22:49:50.060 に答える
4

f出力パラメーターを値で返すように書き直すことができない場合でも、パラメーターパックの拡張を行うのはかなり簡単です。

struct pass { template<typename ...T> pass(T...) {} };

template<typename... Args>
void h(Args... args)
{
    const size_t nargs = sizeof...(args); // get number of args
    X x_array[nargs]; // create X array of that size

    X *x = x_array;
    int unused[]{(f(*x++, args), 1)...}; // call f
    pass{unused};

    g(x_array, nargs); // call g with x_array
}

書くだけで可能になるはずです

    pass{(f(*x++, args), 1)...}; // call f

しかし、g ++(少なくとも4.7.1)には、クラス初期化子としてbrace-initializer-listパラメーターの評価を順序付けできないというバグがあるようです。ただし、配列初期化子は問題ありません。詳細と例については、可変個引数拡張間のシーケンスを参照してください。

実例


別の方法として、生成されたインデックスパックを使用してXeoが言及した手法を次に示します。残念ながら、追加の関数呼び出しとパラメーターが必要ですが、かなりエレガントです(特に、インデックスパックジェネレーターが存在する場合)。

template<int... I> struct index {
    template<int n> using append = index<I..., n>; };
template<int N> struct make_index { typedef typename
    make_index<N - 1>::type::template append<N - 1> type; };
template<> struct make_index<0> { typedef index<> type; };
template<int N> using indexer = typename make_index<N>::type;

template<typename... Args, int... i>
void h2(index<i...>, Args... args)
{
    const size_t nargs = sizeof...(args); // get number of args
    X x_array[nargs]; // create X array of that size

    pass{(f(x_array[i], args), 1)...}; // call f

    g(x_array, nargs); // call g with x_array
}

template<typename... Args>
void h(Args... args)
{
  h2(indexer<sizeof...(args)>(), std::forward<Args>(args)...);
}

C ++ 11を参照してください:複数の引数からタプルに移動できますが、タプルから複数の引数に移動できますか?詳細については。 実例

于 2012-08-19T23:46:09.873 に答える
0

Xeo は正しい考えに基づいています。コードの残りの部分から多くのこの厄介さを隠す、ある種の「可変長イテレータ」を構築したいと考えています。

std::tuple はデータの線形コンテナーでもあるため、インデックスのものを取得し、std::vector をモデルにしたイテレーター インターフェイスの背後に隠します。次に、他の場所に明示的に再帰的なコードを持たなくても、すべての可変引数関数とクラスを再利用できます。

于 2013-05-01T15:58:47.067 に答える