std::ranges::remove, std::ranges::remove_if
| ヘッダー <algorithm> で定義 |
||
| 呼び出しシグネチャ |
||
| (1) | ||
template< std::permutable I, std::sentinel_for<I> S, class T, class Proj = std::identity > |
(C++20以降) (C++26まで) |
|
| template< std::permutable I, std::sentinel_for<I> S, class Proj = std::identity, |
(C++26以降) | |
| (2) | ||
| template< ranges::forward_range R, class T, class Proj = std::identity > |
(C++20以降) (C++26まで) |
|
| template< ranges::forward_range R, class Proj = std::identity, |
(C++26以降) | |
template< std::permutable I, std::sentinel_for<I> S, class Proj = std::identity, |
(3) | (C++20以降) |
| template< ranges::forward_range R, class Proj = std::identity, |
(4) | (C++20以降) |
範囲 [first, last) から指定された基準を満たすすべての要素を削除し、新しい範囲の終端を示すイテレータを `ret` とする範囲 [ret, last) を返します。
削除は、削除されない要素が範囲の先頭に現れるように要素を(ムーブ代入によって)移動させることによって行われます。残る要素の相対順序は保持され、コンテナの**物理的**サイズは変更されません。新しい**論理的**な終端と**物理的**な終端の間の要素を指すイテレータは、依然としてデリファレンス可能ですが、要素自体の値は未定義です(MoveAssignable の事後条件による)。
このページで説明されている関数のようなエンティティは、アルゴリズム関数オブジェクト(非公式にはニーブロイドとして知られている)です。つまり、
- これらのいずれかを呼び出す際に、明示的なテンプレート引数リストを指定することはできません。
- これらのいずれも実引数依存の名前探索には見えません。
- これらのいずれかが関数呼び出し演算子の左側の名前として通常の非修飾名探索によって見つかった場合、実引数依存の名前探索は抑制されます。
目次 |
[編集] パラメータ
| first, last | - | 処理する要素の範囲を定義するイテレータとセンチネルのペア |
| r | - | 処理する要素の範囲 |
| value | - | 削除する要素の値 |
| pred | - | 射影された要素に適用する述語 |
| proj | - | 要素に適用する射影 |
[編集] 戻り値
{ret, last}、ここで [first, ret) は削除後の結果のサブ範囲であり、サブ範囲 [ret, last) 内の要素はすべて有効ですが未定義の状態です。つまり、[ret, last) は消去されるべきサブ範囲です。
[編集] 計算量
対応する述語および射影の適用回数は正確に N 回、ここで N = ranges::distance(first, last) です。ムーブ操作は最大 N - 1 回です。
[編集] 注記
通常、`ranges::remove` の呼び出しの後には、コンテナの `erase` メンバ関数の呼び出しが続きます。これにより、未定義の値が消去され、コンテナの**物理的**サイズが新しい**論理的**サイズに一致するように縮小されます。これら 2 つの呼び出しを組み合わせたものが、いわゆるerase-remove イディオムです。これは、すべての標準**シーケンス**コンテナにオーバーロードを持つフリー関数 std::erase、または**すべての**標準コンテナにオーバーロードを持つ std::erase_if によって実現できます。
同様の名前を持つコンテナのメンバ関数である list::remove、list::remove_if、forward_list::remove、および forward_list::remove_if は、削除された要素を消去します。
これらのアルゴリズムは、通常、std::set や std::map のような連想コンテナでは使用できません。なぜなら、これらのコンテナのイテレータ型は、MoveAssignable 型にはデリファレンスできない(これらのコンテナのキーは変更できない)からです。
`ranges::remove` は `value` を参照で受け取るため、`value` が範囲 [first, last) の要素への参照である場合、予期しない動作をする可能性があります。
[編集] 実装例
| remove (1,2) |
|---|
struct remove_fn { template<std::permutable I, std::sentinel_for<I> S, class Proj = std::identity, class T = std::projected_value_t<I, Proj>> requires std::indirect_binary_predicate <ranges::equal_to, std::projected<I, Proj>, const T*> constexpr ranges::subrange<I> operator()(I first, S last, const T& value, Proj proj = {}) const { first = ranges::find(std::move(first), last, value, proj); if (first != last) { for (I i{std::next(first)}; i != last; ++i) if (value != std::invoke(proj, *i)) { *first = ranges::iter_move(i); ++first; } } return {first, last}; } template<ranges::forward_range R, class Proj = std::identity, class T = std::projected_value_t<ranges::iterator_t<R>, Proj>> requires std::permutable<ranges::iterator_t<R>> && std::indirect_binary_predicate <ranges::equal_to, std::projected<ranges::iterator_t<R>, Proj>, const T*> constexpr ranges::borrowed_subrange_t<R> operator()(R&& r, const T& value, Proj proj = {}) const { return (*this)(ranges::begin(r), ranges::end(r), value, std::move(proj)); } }; inline constexpr remove_fn remove {}; |
| remove_if (3,4) |
struct remove_if_fn { template<std::permutable I, std::sentinel_for<I> S, class Proj = std::identity, std::indirect_unary_predicate<std::projected<I, Proj>> Pred> constexpr ranges::subrange<I> operator()(I first, S last, Pred pred, Proj proj = {}) const { first = ranges::find_if(std::move(first), last, pred, proj); if (first != last) { for (I i{std::next(first)}; i != last; ++i) if (!std::invoke(pred, std::invoke(proj, *i))) { *first = ranges::iter_move(i); ++first; } } return {first, last}; } template<ranges::forward_range R, class Proj = std::identity, std::indirect_unary_predicate <std::projected<ranges::iterator_t<R>, Proj>> Pred> requires std::permutable<ranges::iterator_t<R>> constexpr ranges::borrowed_subrange_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const { return (*this)(ranges::begin(r), ranges::end(r), pred, std::move(proj)); } }; inline constexpr remove_if_fn remove_if {}; |
[編集] 注記
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_lib_algorithm_default_value_type |
202403 |
(C++26) | アルゴリズム (1,2) のためのリスト初期化 |
[編集] 例
#include <algorithm> #include <cassert> #include <complex> #include <cctype> #include <iomanip> #include <iostream> #include <string> #include <string_view> #include <vector> int main() { std::string v1{"No - Diagnostic - Required"}; std::cout << std::quoted(v1) << " (v1, size: " << v1.size() << ")\n"; const auto ret = std::ranges::remove(v1, ' '); std::cout << std::quoted(v1) << " (v1 after `remove`, size: " << v1.size() << ")\n"; std::cout << ' ' << std::string(std::distance(v1.begin(), ret.begin()), '^') << '\n'; v1.erase(ret.begin(), ret.end()); std::cout << std::quoted(v1) << " (v1 after `erase`, size: " << v1.size() << ")\n\n"; // remove_if with custom unary predicate: auto rm = [](char c) { return !std::isupper(c); }; std::string v2{"Substitution Failure Is Not An Error"}; std::cout << std::quoted(v2) << " (v2, size: " << v2.size() << ")\n"; const auto [first, last] = std::ranges::remove_if(v2, rm); std::cout << std::quoted(v2) << " (v2 after `remove_if`, size: " << v2.size() << ")\n"; std::cout << ' ' << std::string(std::distance(v2.begin(), first), '^') << '\n'; v2.erase(first, last); std::cout << std::quoted(v2) << " (v2 after `erase`, size: " << v2.size() << ")\n\n"; // creating a view into a container that is modified by `remove_if`: for (std::string s : {"Small Object Optimization", "Non-Type Template Parameter"}) std::cout << std::quoted(s) << " => " << std::string_view{begin(s), std::ranges::remove_if(s, rm).begin()} << '\n'; std::vector<std::complex<double>> nums{{2, 2}, {1, 3}, {4, 8}}; #ifdef __cpp_lib_algorithm_default_value_type auto e = std::ranges::remove(nums, {1, 3}); // T gets deduced #else auto e = std::ranges::remove(nums, std::complex<double>{1, 3}); #endif nums.erase(e.begin(), e.end()); assert((nums == std::vector<std::complex<double>>{{2, 2}, {4, 8}})); }
実行結果の例
"No _ Diagnostic _ Required" (v1, size: 26) "No_Diagnostic_Requiredired" (v1 after `remove`, size: 26) ^^^^^^^^^^^^^^^^^^^^^^ "No_Diagnostic_Required" (v1 after `erase`, size: 22) "Substitution Failure Is Not An Error" (v2, size: 36) "SFINAEtution Failure Is Not An Error" (v2 after `remove_if`, size: 36) ^^^^^^ "SFINAE" (v2 after `erase`, size: 6) "Small Object Optimization" => SOO "Non-Type Template Parameter" => NTTP
[編集] 関連情報
| (C++20)(C++20) |
特定の基準を満たす要素を除外して範囲をコピーする (アルゴリズム関数オブジェクト) |
| (C++20) |
範囲内の連続する重複要素を削除する (アルゴリズム関数オブジェクト) |
| 特定の基準を満たす要素を削除する (関数テンプレート) |