2017-03-11 10 views
2

私はstd::unique_ptr<Foo>のオブジェクトを持っています。私はいくつかの条件に一致するすべてのベクトル項目のコレクションを取得したい。 私はstd関数を見ていますが、それらはすべて述語(およびboolを返す)をテストするか、単一の要素を返すようです。どのようにして、述語を満たすvector内のunique-ptrsのサブセットを取得できますか?

ベクトルのサブセットであるコレクションを取得する組み込みのメカニズムはありますか?そうでない場合は、任意の述語(自分の条件を満たすものを識別する)とその述語を満たすすべての項目を返すメカニズムに対して項目をテストするイテレータを構築する方法はありますか?

+5

'std :: copy_if 'は要件を満たしていますか? http://en.cppreference.com/w/cpp/algorithm/copy –

+1

返されるサブセットのタイプをどのようにしたいですか?そして元のベクトルは変更されるかどうかですか?あなたは本当にあなたの問題をより正確に指定する必要があります。 –

+0

サブセットをFooポインタのベクトルにしたい。私はオリジナルのベクトルに影響を与えたくありません。サブセットベクトルのアイテムを編集し、それらの編集を元のベクトルに反映させたいと思います。私はポインターを使用していることが確実であると想定しています。 – Craig

答えて

6

unique_ptrのベクトルを持っているので、これらの要素はに移動することができます。つまり、サブセットを取得すると元のベクトルはもう同じになりません。


少なくとも破壊的方法は、同じベクター内のすべてを維持しながら、二つのグループにベクターを分割するstd::stable_partitionを使用することである。

auto sep = std::stable_partition(vec.begin(), vec.end(), [](const auto& foo) { 
    return foo->is_good(); 
}); 
// the part `vec.begin() .. sep` contains all "good" foos. 
// the part `sep .. vec.end()` contains all "bad" foos. 

順序が重要でない場合、use std::partition instead。使い方は同じです。

悪いfoosを別のベクトルに分割する場合は、std::copy_if + std::make_move_iteratorを使用してオブジェクトを移動することができます。これはどこにでも穴が残ることに注意してください。それらをクリーニングするにはstd::removeを使用してください。

decltype(vec) bad_vec; 
std::copy_if(std::make_move_iterator(vec.begin()), 
      std::make_move_iterator(vec.end()), 
      std::back_inserter(bad_vec), 
      [](const auto& p) { return !p->is_good(); }); 
auto new_end = std::remove(vec.begin(), vec.end(), nullptr); 
vec.erase(new_end, vec.end()); 

あなたは、もはや、「悪い」のオブジェクトを気にしないstd::remove_ifを使用する場合:あなたは、可能性が

auto new_end = std::remove_if(vec.begin(), vec.end(), [](const auto& foo) { 
    return !foo->is_good(); 
}); 
vec.erase(new_end, vec.end()); 
// now `vec` only contains "good" foos. 

あなただけの生のポインタ、代わりのunique_ptrを自分自身を取得したい場合std::transformを使用してvector<Foo*>を入力してからremove_ifをフィルタリングしてください...しかし、この時点ではおそらくforループを書く方が簡単でしょう。

std::vector<int*> good_vec; 
for (const auto& foo : vec) { 
    if (foo->is_good()) { 
     good_vec.push_back(foo.get()); 
    } 
} 
+0

'std :: reference_wrapper'のベクトルまたは元のベクトルへのインデックスは、他の非破壊的なオプションです。 – Yakk

2

あなたのベクトルは(私たちはのコピーを作成していない)unique_ptr年代を保持しているので - 私はあなたが尋ね番目のオプションをお勧めしたい:あなただけの述語に一致する要素を反復処理する反復子を。これはまさにboost::filter_iteratorです。

ソート・オブ・例:

bool points_to_positive(int* ptr) { 
    return ptr != nullptr and *ptr > 0; 
} 

// ... 

std::vector<std::unique_ptr<int>> vec; 

// ... 

auto iterator = boost::make_filter_iterator(
    &points_to_positive, std::begin(vec), std::end(vec) 
); 

、しかし、あなたはその反復を複数回行うことを計画、およびスペースのための時間を取引したくない場合、あなたはおそらくより良いだけコピーすることによって提供されるだろう@ kennytmの最後に提案されたオプションのような実際のポインタを出してください。

+0

unique_ptr要件を削除すると、filter_iteratorのSTLバージョンはありますか? – Craig

+0

@Craig:私の記憶が正しく機能しているとは限りませんが、標準ライブラリはイテレータ関連の機能に欠けています。 ...あなた自身のものを作ることはそれほど難しいことではありません。これはかなりのボイラープレートですが、通常のイテレーターと同じですが、 'operator ++'、 'operator +'などの実装は、述語を満たす要素を数えながら進んでいます。 – einpoklum

1

std::copy_if<algorithm>です。コピーすることができないunique_ptr要素の場合、これはあなたが望むものではありません。サンプルコード:

#include <algorithm> 
#include <array> 
#include <cstdlib> 
#include <experimental/array> 
#include <iostream> 
#include <type_traits> 
#include <vector> 

using std::cout; 
using std::endl; 
using std::size_t; 

bool is_even(const int n) 
{ 
    // True iff n is even. 
    return n % 2 == 0; 
} 

std::ostream& operator<< (std::ostream& os, const std::vector<int>& container) 
{ 
    // Boilerplate instrumentation. 
    for (const int& x : container) 
    os << x << ' '; 

    return os; 
} 

int main(void) 
{ 
    // Our input array, raw: 
    constexpr int digits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
    // The number of input elements: 
    constexpr size_t ndigits = std::extent<decltype(digits)>(); 
    // Container wrapping our input array: 
    constexpr std::array<int, ndigits > numbers = 
    std::experimental::to_array(digits); 
    std::vector<int> even_numbers; 

    even_numbers.reserve(ndigits); // Upper bound on output size. 
    std::copy_if(numbers.cbegin(), 
       numbers.cend(), 
       std::back_inserter(even_numbers), 
       is_even); 
    even_numbers.shrink_to_fit(); 

    // Correct output is "2 4 6 8 " 
    cout << even_numbers << endl; 

    return EXIT_SUCCESS; 
} 

しかし、あなたの配列をコピーすることはできませんunique_ptrオブジェクトが含まれています。いくつかの答えは同等の結果を得るための他の良い提案をしています。ただし、要件を満たす参照を別のコレクションにコピーする場合は、unique_ptrshared_ptrまたはweak_ptrに変更してコピーすることもできます。

+0

OPが 'unique_ptr'のコピーを作ることを提案してもよろしいですか? – einpoklum

+0

下部に行を追加しました。いくつかの答えが良い選択肢を与えてくれます!それらの答えは素晴らしいですし、私はそれらを繰り返す必要はないと思っています。私はOPが尋ねた質問のデモコードをいくつか与えただけです。 – Davislor

+1

あなたの提案はうまくいくでしょうが、 'unique_ptr'がどのようにコピーできないのか見ていますか? (私はあなたにdownvotesをここに保存しようとしています...) – einpoklum

関連する問題