従来のアプローチはコピー オン ライトです。このためには、割り当てられた各ノードを参照カウントする必要があります。
変更されたノードの refcount が 1 の場合 (他の誰も参照していない場合)、複製する必要はありません。
これに関する実際的な問題は、変更を非変更操作から便利に分離することです。
char& rope::operator[] (std::string::pos)
参照された char を変更する可能性がありますが、const オーバーロードが実際に変更されない場合に強制的に選択する簡単な方法はありません。これは、ミューテーションが発生すると想定し、おそらく不要なコピーをトリガーするか、代わりに文字変換と代入をオーバーロードするプロキシを返す必要があることを意味します。
このアプローチは、std::string
(文字列が単一ノードのロープと同等である) iirc の初期の実装で試行され、支持されなくなりました。部分的にはミューテーションの問題のためであり、部分的にはマルチスレッドについて心配しなければならない場合、COW と必要な参照カウントがますます高価になるためです。
あなたが言うように、ノードに2つの独立したタイプの状態が含まれている場合、ロープには追加の問題があります。それは、独自の文字列とその子ノードへの参照です(これにより、refcount /子参照の突然変異がツリーを伝播するため)。
代わりに、文字がリーフ ノードにのみ格納され、非リーフ ノードの完全なコピーを行う場合 (したがって、各ロープには独自の "ディレクトリ構造" があります)、文字のコピーを回避し、参照カウントされた共有状態が大幅に増加します。より簡単です。
これは、あなたが望む対数時間の連結を取得しますか? おそらくそうではありません:
- すべての非リーフ ノードをコピーする (そして新しいルートを追加する) 必要があり、これらの数はリーフの数を記録します。
- ただし、リーフの参照カウントをインクリメントする必要もありますが、これは線形です
線形時間に近いか対数時間に近いかは、refcount をインクリメントする場合とディレクトリ ツリーをコピーする場合の相対的なコストに依存します。
ただし、これがないと、高速な連結が得られますが、COW 操作をツリーに伝播する必要がある場合、任意の文字アクセスが (予期せず) 対数時間に劣化する可能性があります。
ユースケースに適している場合は、移動連結を実装できると思います。これにより、一定時間の追加が可能になり、余分な COW の複雑さを回避できます。