範囲のある射影

範囲ライブラリのアルゴリズムは遅延型で、コンテナーで直接動作し、簡単に構成できます。しかし、彼らが提供できるものは他にもあります。予測です。プロジェクションは、サブセットへのセットのマッピングです。この投稿で、それが何を意味するかをお見せしましょう:

前回の投稿「C++20 の Ranges ライブラリ:詳細」を std::sort の比較で締めくくりました と std::ranges::sort . std::ranges::sort の 2 つのオーバーロードを次に示します。 :

 
template <std::random_access_iterator I, std::sentinel_for<I> S,
 class Comp = ranges::less, class Proj = std::identity>
requires std::sortable<I, Comp, Proj>
constexpr I sort(I first, S last, Comp comp = {}, Proj proj = {});

template <ranges::random_access_range R, class Comp = ranges::less, 
 class Proj = std::identity>
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr ranges::borrowed_iterator_t<R> sort(R&& r, Comp comp = {}, Proj proj = {});

最初のオーバーロードを調べると、ソート可能な範囲 R が必要であることがわかります。 、述語 Comp 、および射影 Proj .述語 Comp デフォルトの範囲に使用::less、および射影 Proj 引数を変更せずに返すアイデンティティ std::identity。 std::identity C++20 で追加されたのは、ヘッダー で定義された関数オブジェクトです。要するに、コンポーネントは次のとおりです。
  • コンパレータ:Comp (ブール値を返すバイナリ関数)
  • 射影:Proj (サブセットへのセットのマッピング)
  • センチネル: std::sentinel_for<I> (シーケンスの終わりを示す特別な値)
  • コンセプト:std::random_access_iterator, std::sortable<I, Comp, Proj> 、および std::sentinel_for<I>

対照的に、2 番目のオーバーロードは Iterator I を返しませんが、 ranges::borrowed_iterator_t を返します。 .もちろん、これも概念であり、返されたイテレータが後で安全に使用できることを保証します。したがって、この反復子を安全な反復子と呼びます。 std::ranges::borrowed_iterator_tについて詳しく書きます

プロジェクションは、サブセットへのセットのマッピングです。これはどういう意味ですか?

射影

範囲ライブラリのアルゴリズムは、コンテナーで直接動作します。これは、プロジェクションがデフォルトで std::identity であるためです。次の例では、データ型 PhoneBookEntry にプロジェクションを適用します。 .
// rangeProjection.cpp

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
 
struct PhoneBookEntry{ // (1)
 std::string name;
 int number;
};

void printPhoneBook(const std::vector<PhoneBookEntry>& phoneBook) {
 for (const auto& entry: phoneBook) std::cout << "(" << entry.name << ", " 
 << entry.number << ")";
 std::cout << "\n\n";
}
 
int main() {

 std::cout << '\n';

 std::vector<PhoneBookEntry> phoneBook{ {"Brown", 111}, {"Smith", 444}, 
 {"Grimm", 666}, {"Butcher", 222}, {"Taylor", 555}, {"Wilson", 333} };

 std::ranges::sort(phoneBook, {}, &PhoneBookEntry::name); // ascending by name (2)
 printPhoneBook(phoneBook);

 std::ranges::sort(phoneBook, std::ranges::greater() , 
 &PhoneBookEntry::name); // descending by name (3)
 printPhoneBook(phoneBook);

 std::ranges::sort(phoneBook, {}, &PhoneBookEntry::number); // ascending by number (4)
 printPhoneBook(phoneBook);

 std::ranges::sort(phoneBook, std::ranges::greater(), 
 &PhoneBookEntry::number); // descending by number (5)
 printPhoneBook(phoneBook);
 
}

phoneBook (1 行目) PhoneBookEntry 型の構造体があります (ライン1)。 PhoneBookEntry name で構成されています そして number .投影のおかげで、phoneBook name で昇​​順にソートできます (2行目) name の降順 (3行目) number で昇​​順 (4行目) number で降順 (5 行目)。式 std::ranges::sort(phoneBook, {}, &PhoneBookEntry::name) の空の中括弧 この場合 std::less であるソート基準のデフォルトの構築を引き起こします .

プロジェクションがより要求の厳しい場合は、ラムダ式などの呼び出し可能オブジェクトを使用できます。

// rangeProjectionCallable.cpp

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
 
struct PhoneBookEntry{ 
 std::string name;
 int number;
};

void printPhoneBook(const std::vector<PhoneBookEntry>& phoneBook) {
 for (const auto& entry: phoneBook) std::cout << "(" << entry.name << ", " 
 << entry.number << ")";
 std::cout << "\n\n";
}
 
int main() {

 std::cout << '\n';

 std::vector<PhoneBookEntry> phoneBook{ {"Brown", 111}, {"Smith", 444}, 
 {"Grimm", 666}, {"Butcher", 222}, {"Taylor", 555}, {"Wilson", 333} };

 std::ranges::sort(phoneBook, {}, &PhoneBookEntry::name);  // (1)
 printPhoneBook(phoneBook);

 std::ranges::sort(phoneBook, {}, [](auto p){ return p.name; } );  // (2)
 printPhoneBook(phoneBook);

 std::ranges::sort(phoneBook, {}, [](auto p) {  // (3)
 return std::to_string(p.number) + p.name; 
 });
 printPhoneBook(phoneBook);

 std::ranges::sort(phoneBook, [](auto p, auto p2) {  // (4)
 return std::to_string(p.number) + p.name < 
 std::to_string(p2.number) + p2.name; 
 });
 printPhoneBook(phoneBook);
 
}

std::ranges::sort 行(1)では、属性 PhoneBookEntry::name を使用しています 投影として。行 (2) は、同等のラムダ式 [](auto p){ return p.name; } を示しています。 投影として。行 (3) の射影はより厳しいものです。 p.name. に連結された文字列化された数値を使用します もちろん、連結された文字列化された数値と名前を並べ替え基準として直接使用できます。この場合、(3) 行のアルゴリズム呼び出しは、(4) 行のアルゴリズム呼び出しよりも読みやすくなっています。これを強調したい。行 (3) はソート基準として射影を使用しますが、行 (4) はパラメーター化された std::ranges::sort ラムダ式で与えられる二項述語で。 ほとんどの範囲アルゴリズムは射影をサポートしています。

次は?

次の投稿では、センチネルについて書きます。範囲の終わりを指定し、一般化された終了イテレータと見なすことができます。