COWbasic_string
は C++11 以降で禁止されていますか?
それにかんする
C++11 が の COW ベースの実装を認めないというのは正しいですstd::string
か?
はい。
それにかんする
もしそうなら、この制限は新しい標準のどこかに明示的に記載されていますか?
COW 実装でO( n ) 文字列データの物理コピーを必要とする多数の操作の一定の複雑さの要件により、ほぼ直接的です。
たとえば、メンバー関数の場合
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
… COW 実装では、どちらも文字列データのコピーをトリガーして文字列値の共有を解除します。C++11 標準では、
C++11 §21.4.5/4 :
複雑さ:一定時間。
…そのようなデータのコピーを除外するため、COW.
C++03 は、これらの一定の複雑さの要件を持たないことによって COW 実装をサポートし、特定の制限条件下で、 、 、 、 への呼び出しを許可しoperator[]()
たりat()
、begin()
文字rbegin()
列end()
項目rend()
を参照する参照、ポインター、およびイテレーターを無効にしたり、つまり、 COW データのコピー。このサポートは C++11 で削除されました。
COW は C++11 の無効化規則によっても禁止されていますか?
執筆時点で解決策として選択されており、非常に支持されているため明らかに信じられている別の回答では、次のように主張されています。
」 COW 文字列の場合、 non-const
operator[]
を呼び出すには、[C++11 §21.4.1/6] の上の [引用] 段落で許可されていないコピーを作成する (および参照を無効にする) 必要があります。したがって、C++11 で COW 文字列を使用することはもはや合法ではありません。
その主張は正しくなく、主に次の 2 つの点で誤解を招きます。
const
非item アクセサーのみが COW データ コピーをトリガーする必要があることを誤って示しています。
ただし、const
アイテム アクセサーもデータ コピーをトリガーする必要があります。これは、クライアント コードが (C++11 では) COW データ コピーをトリガーできる操作を介して後で無効にすることが許可されていない参照またはポインターを形成できるようにするためです。
- COW データのコピーが参照の無効化を引き起こす可能性があると誤って想定しています。
しかし、正しい実装では、COW データのコピー (文字列値の共有解除) は、無効化できる参照が存在する前の時点で行われます。
の正しい C++11 COW 実装がどのように機能するかを確認するにはbasic_string
、これを無効にする O(1) 要件が無視されたときに、文字列が所有権ポリシーを切り替えることができる実装を考えてみてください。文字列インスタンスはポリシー Sharable で始まります。このポリシーをアクティブにすると、外部アイテム参照ができなくなります。.c_str()
インスタンスは Unique ポリシーに移行することができ、 (少なくとも内部バッファーへのポインターを生成する場合)への呼び出しなどでアイテム参照が作成される可能性がある場合は、そうする必要があります。複数のインスタンスが値の所有権を共有する一般的なケースでは、これには文字列データのコピーが伴います。Unique ポリシーへの移行後、インスタンスは、割り当てなどのすべての参照を無効にする操作によってのみ Sharable に移行できます。
したがって、COW 文字列が除外されるというその回答の結論は正しいですが、提示された推論は正しくなく、誤解を招く可能性があります。
この誤解の原因は、C++11 の附属書 C の非規範的な注記にあると思われます。
C++11 §C.2.11 [diff.cpp03.strings]、§21.3 について:
変更:basic_string
要件で参照カウント文字列が許可されなくなりました
根拠:参照カウント文字列では無効化が微妙に異なります。この変更により、この国際規格の動作 (原文ママ) が正規化されます。
元の機能への影響:有効な C ++ 2003 コードは、この国際標準では異なる方法で実行される場合があります
ここでは、C++03 の特別な COW サポートを削除することにした主な理由を説明します。この論理的根拠である理由は、標準が COW の実装を事実上禁止する方法ではありません。標準では、O(1) 要件による COW は許可されていません。
要するに、C++11 の無効化規則は の COW 実装を除外しませんstd::basic_string
。しかし、g++ の標準ライブラリの実装の少なくとも 1 つにあるような、合理的に効率的な制限のない C++03 スタイルの COW 実装を除外しています。特別な C++03 COW サポートによりconst
、無効化のための微妙で複雑なルールを犠牲にして、特に項目アクセサーを使用して実用的な効率が可能になりました。
「最初の呼び出し」COW サポートを含む
C++03 §21.3/5 :
シーケンスの要素を参照する参照、ポインター、および反復子は、そのオブジェクトbasic_string
を次のように使用すると無効になる場合があります。
— 非メンバー関数(21.3.7.8)、 (21. 3.7.9)、および(21.3. 7.9)。
— への引数として。
— 呼び出し関数とメンバー関数。— 、、、、、およびを除く
非メンバー関数の呼び出し。—イテレータを返すおよび
の形式を除く上記の使用のいずれかに続いて、非メンバー関数, , ,への最初の呼び出しbasic_string
swap()
operator>>()
getline()
basic_string::swap()
data()
c_str()
const
operator[]()
at()
begin()
rbegin()
end()
rend()
insert()
erase()
const
operator[]()
at()
begin()
rbegin()
end()
、またはrend()
。
これらのルールは非常に複雑で微妙なので、正確な要約を提供できるプログラマーがいるとは思えません。私ができなかった。
O(1) 要件が無視された場合はどうなりますか?
たとえば、C++11 の一定時間の要件operator[]
が無視される場合、COW はbasic_string
技術的には実現可能ですが、実装するのは困難です。
COW データのコピーを発生させずに文字列の内容にアクセスできる操作には、次のものがあります。
- による連結
+
。
- 経由で出力し
<<
ます。
basic_string
標準ライブラリ関数の引数としてa を使用します。
後者は、標準ライブラリが実装固有の知識と構造に依存することが許可されているためです。
さらに、実装によって、COW データのコピーをトリガーせずに文字列の内容にアクセスするためのさまざまな非標準関数を提供できます。
主な複雑な要因は、C++11 ではbasic_string
item アクセスがデータのコピー (文字列データの非共有) をトリガーする必要があることですが、たとえば、C++11 §21.4.5/3 “ Throws: Nothing.”をスローしないようにする必要があります。そのため、通常の動的割り当てを使用して COW データ コピー用の新しいバッファーを作成することはできません。これを回避する 1 つの方法は、実際に割り当てることなくメモリを予約できる特別なヒープを使用し、文字列値への論理参照ごとに必要な量を予約することです。そのようなヒープでの予約と予約解除は一定時間、O(1) であり、すでに予約されている量の割り当ては、noexcept
. 標準の要件に準拠するために、このアプローチでは、個別のアロケーターごとに 1 つの特別な予約ベースのヒープが必要になるようです。
注:
¹アイテム アクセサーは COW データ コピーをトリガーします。これは、クライアント コードがデータへの参照またはポインターを取得できるようにするためです。これは、非アイテム アクセサーなど
const
によってトリガーされる後のデータ コピーによって無効にすることは許可されていません。const