2017-01-24 3 views
3

私は可変長テンプレート関数foo()を持っている:C++パラメータパックをstd :: pairオブジェクトのシーケンスにマップするにはどうすればよいですか?

template <typename... Args> 
void foo(Args &&... args); 

この関数はsize_tのすべての引数で呼び出されることを意図しています。私はsome metaprogrammingを使って強制することができます。結果のリストを一度に2つずつ取り出して、std::pair<size_t, size_t>のコンテナに入れておく必要があります。概念的には、のようなもの:

std::vector<std::pair<size_t, size_t> > = { 
    std::make_pair(args[0], args[1]), 
    std::make_pair(args[2], args[3]), ... 
}; 

は、これを行うための簡単な方法はありますか?私はパックの拡張によって、引数をフラットコンテナに入れることができることを知っていますが、同時に2つずつ2つずつグループ化する方法がありますか?

+0

"*これは簡単な方法ですか?*"いいえ。 –

+1

2つの引数を消費する再帰型挿入子を作成し、2つのストップケース特殊化を使用してベクトルとしてペアにpush_backすることができます。 – luk32

+0

追加の 'std :: index_sequence 'パラメータを持つ内部ヘルパー関数に転送する可能性はありますか? –

答えて

6

パックへのインデックス付けは実際には実行できません(まだですか?)が、タプルへのインデックス付けはです。最初にタプルにすべてを貼り付けてから、すべてのものを取り除きます。すべてがsize_tなので、私たちはコピーすることができます。

template <size_t... Is, class Tuple> 
std::vector<std::pair<size_t, size_t>> 
foo_impl(std::index_sequence<Is...>, Tuple tuple) { 
    return std::vector<std::pair<size_t, size_t> >{ 
     std::make_pair(std::get<2*Is>(tuple), std::get<2*Is+1>(tuple))... 
    }; 
} 

template <typename... Args> 
void foo(Args... args) 
{ 
    auto vs = foo_impl(std::make_index_sequence<sizeof...(Args)/2>{}, 
     std::make_tuple(args...)); 

    // ... 
} 
+0

たぶん 'std :: forward_as_tuple'ですか? –

+0

@KerrekSB彼らはすべて 'size_t'sです。コンパイラがとにかく 'tuple'を削除することは間違いありません。 – Barry

2

は、あなたが内部ヘルパー関数にあなたのロジックをリファクタリングすることが許可されていると仮定します

template <typename ...Args> 
void foo(Args &&... args) 
{ 
    foo_impl(std::make_index_sequence<sizeof...(Args)/2>(), 
      std::forward<Args>(args)...); 
} 

今、私たちはインデックスで引数パックインデックスを操作することができます:

template <std::size_t ...I, typename ...Args> 
void foo_impl(std::index_sequence<I...>, Args &&... args) 
{ 
    std::vector<std::pair<std::size_t, std::size_t>> v = 
     { GetPair(std::integral_constant<std::size_t, I>(), args...)... }; 
} 

これは、ペアの抽出を実現するために残っている:

template <typename A, typename B, typename ...Tail> 
std::pair<std::size_t, std::size_t> GetPair(std::integral_constant<std::size_t, 0>, 
              A a, B b, Tail ... tail) 
{ 
    return { a, b }; 
} 

template <std::size_t I, typename A, typename B, typename ...Tail> 
std::pair<std::size_t, std::size_t> GetPair(std::integral_constant<std::size_t, I>, 
              A a, B b, Tail ... tail) 
{ 
    return GetPair<I - 1>(tail...); 
} 
+0

優れています。私はバリーの答えをもう少し簡潔にして受け入れましたが、これは良い解決策のようにも見えます。 –

+0

@JasonR:はい、 'std :: get'は、私の' GetPair'が生パックであるタプルに対して効果的です。タプルはこの種の汎用コードにとって非常に強力です。 –

2

range-v3では、あなたが行うこと

template <typename... Args> 
void foo(Args&&... args) 
{ 
    std::initializer_list<std::size_t> nbs = {static_cast<std::size_t>(args)...}; 
    const auto pair_view = 
     ranges::view::zip(nbs | ranges::view::stride(2), 
          nbs | ranges::view::drop(1) | ranges::view::stride(2)); 

    // And possibly 
    std::vector<std::pair<std::size_t, std::size_t>> pairs = pair_view; 
    // ... 
} 

Demo

+0

の範囲はきれいです:) –

+0

最後の行は、次のようにニシンにすることができます:auto pairs = pair_view | transform({std :: make_pair(...);})を返します。範囲:: to_vector; –

+0

私はこれが大好きです。もともと私はこれらのラインに沿って考えていましたが、範囲が標準になるまで待たなければなりません! –

1

誰か( @Barry )がパックにインデックス作成が不可能であることを述べました。

これはC++です。不可能とは、まだ書いていないということです。

template<std::size_t I> struct index_t:std::integral_constant<std::size_t, I> { 
    using std::integral_constant<std::size_t, I>::integral_constant; 
    template<std::size_t J> 
    constexpr index_t<I+J> operator+(index_t<J>) const { return {}; } 
    template<std::size_t J> 
    constexpr index_t<I-J> operator-(index_t<J>) const { return {}; } 
    template<std::size_t J> 
    constexpr index_t<I*J> operator*(index_t<J>) const { return {}; } 
    template<std::size_t J> 
    constexpr index_t<I/J> operator/(index_t<J>) const { return {}; } 
}; 
template<std::size_t I> 
constexpr index_t<I> index{}; 

template<std::size_t B> 
constexpr index_t<1> exponent(index_t<B>, index_t<0>) { return {}; } 

template<std::size_t B, std::size_t E> 
constexpr auto exponent(index_t<B>, index_t<E>) { 
    return index<B> * exponent(index<B>, index<E-1>); 
} 
template<std::size_t N> 
constexpr index_t<0> from_base(index_t<N>) { return {}; } 
template<std::size_t N, std::size_t c> 
constexpr index_t<c-'0'> from_base(index_t<N>, index_t<c>) { return {}; } 
template<std::size_t N, std::size_t c0, std::size_t...cs> 
constexpr auto from_base(index_t<N>, index_t<c0>, index_t<cs>...) { 
    return 
    from_base(index<N>, index<c0>) * exponent(index<N>, index<sizeof...(cs)>) 
    + from_base(index<N>, index<cs>...) 
    ; 
} 

template<char...cs> 
constexpr auto operator""_idx(){ 
    return from_base(index<10>, index<cs>...); 
} 

auto nth = [](auto index_in){ 
    return [](auto&&...elems)->decltype(auto){ 
    using std::get; 
    constexpr auto I= index<decltype(index_in){}>; 
    return get<I>(std::forward_as_tuple(decltype(elems)(elems)...)); 
    }; 
}; 

は今我々が得る:コンパイル時定数インデックスを使用して、当社のパラメータパックに

using pair_vec = std::vector<std::pair<std::size_t, std::size_t>>; 
template <typename... Args> 
pair_vec foo(Args &&... args) { 
    return 
    index_over< sizeof...(args)/2 >() 
    ([&](auto...Is)->pair_vec{ 
     return { 
     { 
      nth(Is*2_idx)(decltype(args)(args)...), 
      nth(Is*2_idx+1_idx)(decltype(args)(args)...) 
     }... 
     }; 
    }); 
} 

我々「直接」のインデックスを。

live example

+0

非常に印象的です。私は現代のC++カーブの数年後になっているので、このような解決策を最初から夢見ることは難しいです。ニースのデモンストレーション。 –

関連する問題