yohhoyの日記

技術的メモをしていきたい日記

Range挿入操作と要素オーバーラップ

C++23標準ライブラリのシーケンスコンテナ*1に対するRange挿入操作では、挿入先コンテナと挿入元Rangeとの要素オーバーラップが{append,prepend}_range操作でのみ許容される。それ以外のメンバ関数で要素オーバーラップしていた場合は未定義動作(undefined behavior)を引き起こす。

  • append_range:OK
  • prepend_range:OK
  • assign_range:NG
  • insert_range:NG
  • insert_range_after:NG

例示コードではstd::vectorに対して同コンテナの一部要素を逆順で末尾追加(append_range)している。*2

#include <ranges>
#include <string_view>
#include <vector>
using namespace std::literals;  // ""sv

std::vector<char> vec{std::from_range, "RACE"sv};
// vec=['R', 'A', 'C', 'E']

// OK: vecの先頭3要素を逆順でvec末尾に追加
vec.append_range(vec | std::views::take(3) | std::views::reverse);
// vec=['R', 'A', 'C', 'E', 'C', 'A', 'R']

C++23 24.2.4, 24.3.9.5よりシーケンスコンテナ操作の事前条件(Preconditions)を引用。

a.insert_range(p, rg)

41 Preconditions: T is Cpp17EmplaceConstructible into X from *ranges::begin(rg). For vector and deque, T is also Cpp17MoveInsertable into X, and T meets the Cpp17MoveConstructible, Cpp17MoveAssignable, and Cpp17Swappable requirements. rg and a do not overlap.

a.assign_range(rg)

62 Preconditions: T is Cpp17EmplaceConstructible into X from *ranges::begin(rg). For vector, if R models neither ranges::sized_range nor ranges::forward_range, T is also Cpp17MoveInsertable into X. rg and a do not overlap.

a.prepend_range(rg)

95 Preconditions: T is Cpp17EmplaceConstructible into X from *ranges::begin(rg). For deque, T is also Cpp17MoveInsertable into X, and T meets the Cpp17MoveConstructible, Cpp17MoveAssignable, and Cpp17Swappable requirements.

a.append_range(rg)

107 Preconditions: T is Cpp17EmplaceConstructible into X from *ranges::begin(rg). For vector, T is also Cpp17MoveInsertable into X.

template<container-compatible-range<T> R>
  iterator insert_range_after(const_iterator position, R&& rg);

18 Preconditions: T is Cpp17EmplaceConstructible into forward_list from *ranges::begin(rg). position is before_begin() or is a dereferenceable iterator in the range [begin(),end()). rg and *this do not overlap.

ノート:C++標準ライブラリ文字列型std::basic_stringassign_range, insert_range(およびappend_range, replace_with_range)操作を提供するが、要素オーバーラップに関する前提条件は存在しない。各関数の効果(Effects)によれば引数Rangeから一時オブジェクトを生成するため、要素オーバーラップしていても問題ない。

関連URL

*1:本記事の対象は std::vector, std::deque, std::list, std::forward_list の4種類。std::array もシーケンスコンテナの一種だが、要素数コンパイル時定数のため動的要素追加をサポートしない。

*2:std::vector コンストラクタ引数で std::string_view を経由するのは、文字列リテラル末尾に含まれるNUL終端文字を除外するため。類似ケース id:yohhoy:20161204 も参照のこと。