4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

C++26 で使える(はずの) std::optional の range インターフェイスについて

Last updated at Posted at 2024-07-16

P3168R1 Give std::optional Range Support が採択されそうなので、紹介します。

概要

std::optional には C++23 でモナディックな操作を提供する変更が入りましたが、またしても利便性を向上させるために range を満たすようなインターフェイスが追加されます。

追加されるメンバなど

この提案書での変更はそう大きくなく、追加されるものをざっと示すと以下のようになります。

namespace std {

template <class T>
class optional {
  using iterator       = /* implementation-defined */;
  using const_iterator = /* implementation-defined */;

  constexpr iterator begin() noexcept;
  constexpr const_iterator begin() const noexcept;
  constexpr iterator end() noexcept;
  constexpr const_iterator end() const noexcept;

  /* existing members... */
};

template <class T>
constexpr bool ranges::enable_view<optional<T>> = true;

template <class T>
constexpr auto format_kind<optional<T>> = range_format::disabled;

}

begin, end

constexpr iterator begin() noexcept;
constexpr const_iterator begin() const noexcept;
constexpr iterator end() noexcept;
constexpr const_iterator end() const noexcept;

std::optional を要素数が 0 または 1 のコンテナであるかのように扱う、メモリ連続(contiguous)なイテレータを提供します。
具体的には、 opt.has_value() が真のとき *opt.begin()*opt と等しく、そうでなければ opt.begin() == opt.end() です。

std::optional<int> opt;

for (auto&& ref : opt) ref *= 2;
// equivalent to
opt.transform([](auto&& ref) { ref *= 2; });

std::ranges::enable_view の特殊化

template <class T>
constexpr bool ranges::enable_view<optional<T>> = true;

ある型を view として扱うためには std::ranges::view_interface<Derived> を継承するか std::ranges::enable_view を特殊化するかの二通り方法がありますが、前者は ABI 互換性を保つのに難があるため、後者の方法が取られます。

static_assert( std::ranges::view<std::optional<int>> );  // C++23 で false, C++26 で true

std::format_kind の特殊化

template <class T>
constexpr auto format_kind<optional<T>> = range_format::disabled;

こちらは一見マイナーな変更ではありますが、P2286R8 Formatting RangesP2585R1 Improve default container formatting などで導入された任意の range を std::format でフォーマットできる機構からオプトアウトするための特殊化です。
これによって std::formatter<std::optional<T>> を明示的に特殊化しない限り std::optionalstd::format によってフォーマットされなくなります。

既存のコードに対する影響について

例えば以下のような C++20 のコードについてです。

#include <concepts>
#include <optional>
#include <ranges>

template <class T>
concept optional_like = requires(T x) {
  { x.has_value() } -> std::convertible_to<bool>;
  { *x } -> std::convertible_to<typename std::remove_cvref_t<T>::value_type const&>;
};

constexpr auto func(auto) { return 0; }                     // #0

constexpr auto func(std::ranges::range auto) { return 1; }  // #1

constexpr auto func(optional_like auto) { return 2; }       // #2

static_assert(func(std::optional<int>(42)) == 2);

#0#2 の3つのオーバーロードを持つ関数テンプレートに対しこの提案が適用される前は std::ranges::rangeoptional-like を同時に満たす型を想定していないために static_assert が通りますが、適用後はちょうど std::optional がその条件を満たしてしまい #1#2 とでオーバーロード解決が曖昧となりコンパイルエラーになります。
こういったトリッキーな型に対する対策が十分に取られていないコードには影響が出ますが、ある程度テスト体制が整っているならば避けやすい影響であると思われます。

備考

元々は P1255R12 A view of 0 or 1 elements: views::maybe といった提案において std::views::maybe という「要素が 0 または 1 個であるような range」が導入されようとしていましたが、そのコンセプトは std::optional と酷似しており、また Rust の Option<T> や Scala の Option[T] などの型がイテレーション操作を提供していることを鑑みて std::optional<T> に range インターフェイスを追加しようという運びになったようです。
また本提案書の著者を含めた標準化委員は P1255R12 で追加を提案されていた std::views::nullable という nullable なオブジェクトを「要素が 0 または 1 個であるような range」化するアダプタ自体は支持しており、次のリビジョンでは std::views::maybe が削除されています。

参照

P3168R1 Give std::optional Range Support
P1255R12 A view of 0 or 1 elements: views::maybe
P1255R13 A view of 0 or 1 elements: views::nullable

4
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?