web-dev-qa-db-ja.com

C ++ 11でのCOW std :: string実装の合法性

Copy-on-writeはC++ 11でstd::stringを実装するための実行可能な方法ではないことを理解していましたが、最近の議論でそのステートメントを直接サポートできないことに気付きました。

C++ 11がstd::stringのCOWベースの実装を許可しないことを修正しますか?

ある場合、この制限は新しい標準のどこか(どこ)で明示的に述べられていますか?

または、COWベースのstd::stringの実装を妨げるのは、std::stringに対する新しい要件の複合効果であるという意味で、この制限が暗示されています。この場合、「C++ 11はCOWベースのstd::string実装を事実上禁止する」という章と詩のスタイルの派生に興味があります。

112
acm

標準の21.4.1 p6に従って、イテレータ/参照の無効化は次の場合にのみ許可されるため、許可されません。

— non-const basic_stringへの参照を引数として取る標準ライブラリ関数への引数として。

— operator []、at、front、back、begin、rbegin、end、およびrendを除く非constメンバー関数の呼び出し。

COW文字列の場合、非const operator[]を呼び出すにはコピーを作成する(および参照を無効にする)必要がありますが、これは上記の段落では禁止されています。したがって、C++ 11でCOW文字列を使用することはもはや正当ではありません。

115
Dave S

Dave S および gbjbaanb による回答はcorrectです。 (また、Luc Dantonも正しいのですが、それはCOW文字列を禁止する本来のルールではなく、COW文字列を禁止する副作用です。)

ただし、混乱を解消するために、さらに説明を加えます。さまざまなコメントが GCC bugzillaに関する私のコメント にリンクしています。これは次の例を示しています。

std::string s("str");
const char* p = s.data();
{
    std::string s2(s);
    (void) s[0];
}
std::cout << *p << '\n';  // p is dangling

その例のポイントは、GCCの参照カウント(COW)文字列がC++ 11で無効な理由を示すことです。 C++ 11標準では、このコードが正しく機能する必要があります。 C++ 11でpを無効にすることを許可するコードはありません。

GCCの古い参照カウントstd::string実装を使用すると、pisが無効になり、ダングリングポインターになるため、そのコードには未定義の動作があります。 (何が起こるかというと、s2が構築されると、データをsと共有しますが、s[0]を介して非const参照を取得するには、データを非共有にする必要があるため、s参照s[0]を使用してsに書き込む可能性があるため、s2がスコープ外になり、pが指す配列を破壊するため、「書き込み時にコピー」を行います。 )。

C++ 03標準は、21.3 [lib.basic.string] p5でその動作を明示的に許可しています。ここでは、data()の呼び出しに続いてoperator[]()を呼び出すと、ポインター、参照、反復子が無効になる場合があります。したがって、GCCのCOW文字列は有効なC++ 03実装でした。

C++ 11標準は、その振る舞いを許可しなくなりました。これは、operator[]()の呼び出しは、呼び出しに従うかどうかに関係なく、ポインター、参照、またはイテレーターを無効にするためです。 data()へ。

したがって、上記の例mustはC++ 11で機能しますが、は機能しませんlibstdc ++の種類のCOW文字列で動作するため、C++ 11ではその種類のCOW文字列は許可されません。

43
Jonathan Wakely

CoWは、より高速な文字列を作成するための許容可能なメカニズムですが...

これにより、マルチスレッドコードが遅くなります(書き込みが1つだけであるかどうかを確認するためのロックは、多くの文字列を使用するとパフォーマンスを低下させます)。これが、CoWが数年前に殺された主な理由でした。

他の理由は、_[]_演算子が文字列データを返すことであり、他の誰かが不変であると期待する文字列を上書きする保護はありません。同じことがc_str()data()にも当てはまります。

クイックグーグルは、マルチスレッドは基本的に 事実上許可されない理由 (明示的ではない)であると言います。

提案は言う:

提案

すべてのイテレータおよび要素アクセス操作を安全に同時に実行可能にすることを提案します。

順次コードでも操作の安定性を向上させています。

この変更により、コピーオンライトの実装が事実上禁止されます。

に続く

コピーオンライトの実装からの切り替えによるパフォーマンスの最大の潜在的な損失は、非常に大きな読み取り主に文字列を持つアプリケーションのメモリ消費の増加です。ただし、これらのアプリケーションではロープがより優れた技術的解決策であると考えており、ロープの提案をライブラリTR2に含めることを検討することをお勧めします。

ロープ は、STLPortおよびSGI STLの一部です。

19
gbjbaanb

21.4.2からbasic_stringコンストラクターと割り当て演算子[string.cons]

basic_string(const basic_string<charT,traits,Allocator>& str);

[...]

2 効果:表64に示すように、クラス_basic_string_のオブジェクトを構築します。[...]

表64は、この(コピー)コンストラクターを介したオブジェクトの構築後、this->data()の値が次のようになることを文書化しています。

str.data()によって最初の要素がポイントされる配列の割り当てられたコピーの最初の要素をポイントします

他の同様のコンストラクターには同様の要件があります。

5
Luc Danton

文字列が連続して格納されることが保証され、文字列の内部ストレージへのポインタを取得できるようになったため(つまり、&str [0]は配列のように機能します)、有用なCOWを作成することはできません実装。あまりにも多くのものをコピーする必要があります。非const文字列で_operator[]_またはbegin()を使用するだけでも、コピーが必要です。

1
Dirk Holsopple