問題
string
文字列を表すクラスを実装するとします。operator+
次に、 2 つの を連結する を追加し、を実行するときに複数の割り当てを回避するために式テンプレートstring
を介して実装することにします。str1 + str2 + ... + strN
オペレーターは次のようになります。
stringbuilder<string, string> operator+(const string &a, const string &b)
stringbuilder
はテンプレート クラスであり、オーバーロードoperator+
し、暗黙的string
な変換演算子を持ちます。ほとんど標準的な教科書の演習:
template<class T, class U> class stringbuilder;
template<> class stringbuilder<string, string> {
stringbuilder(const string &a, const string &b) : a(a), b(b) {};
const string &a;
const string &b;
operator string() const;
// ...
}
// recursive case similar,
// building a stringbuilder<stringbuilder<...>, string>
上記の実装は、誰かがそうする限り完全に機能します
string result = str1 + str2 + ... + strN;
ただし、微妙なバグがあります。結果を正しい型の変数に代入すると、その変数は式を構成するすべての文字列への参照を保持します。これは、たとえば、文字列の 1 つを変更すると結果が変わることを意味します。
void print(string);
string str1 = "foo";
string str2 = "bar";
right_type result = str1 + str2;
str1 = "fie";
print(result);
式テンプレート内に str1 参照が格納されているため、これによりfiebar が出力されます。ひどくなる:
string f();
right_type result = str1 + f();
print(result); // kaboom
これで、式テンプレートに破棄された値への参照が含まれ、プログラムがすぐにクラッシュします。
今それは何right_type
ですか?もちろんstringbuilder<stringbuilder<...>, string>
、それは式テンプレートマジックが生成する型です。
では、なぜそのような隠し型を使用するのでしょうか? 実際、明示的に使用することはありませんが、C++11 の auto は使用します!
auto result = str1 + str2 + ... + strN; // guess what's going on here?
質問
要するに、式テンプレートを実装するこの方法 (値をコピーしたり、共有ポインターを使用したりする代わりに安価な参照を保存することによって) は、式テンプレート自体を保存しようとするとすぐに壊れてしまうようです。
したがって、右辺値または左辺値を構築しているかどうかを検出し、右辺値が構築されている (参照を保持する) か、左辺値が構築されている (コピーを作成する) かによって、式テンプレートの異なる実装を提供する方法がかなり必要です。 )。
この状況を処理するための確立された設計パターンはありますか?
調査中にわかった唯一のことは、
this
左辺値または右辺値に応じて、メンバー関数をオーバーロードできます。class C { void f() &; void f() &&; // called on temporaries }
ただし、コンストラクターでもそれを行うことはできないようです。
C++ では、実際には「型オーバーロード」を行うことはできません。つまり、型の使用方法(左辺値または右辺値として作成されたインスタンス) に応じて、同じ型の複数の実装を提供することはできません。