C++11 に移行したので、コンストラクターで体系的に文字列を値で渡すようになりました。しかし今では、コンストラクターの本体でも値を使用すると、バグが発生しやすくなることがわかりました。
class A(std::string val):
_val(std::move(val))
{
std::cout << val << std::endl; // Bug!!!
}
間違える確率を下げるにはどうしたらいいですか?
C++11 に移行したので、コンストラクターで体系的に文字列を値で渡すようになりました。しかし今では、コンストラクターの本体でも値を使用すると、バグが発生しやすくなることがわかりました。
class A(std::string val):
_val(std::move(val))
{
std::cout << val << std::endl; // Bug!!!
}
間違える確率を下げるにはどうしたらいいですか?
少なくともコンストラクターの実装内で、何らかの特徴的な方法で移動元となる名前の引数
A::A(std::string val_moved_from):
_val(std::move(val_moved_from))
{
std::cout << val_moved_from << std::endl; // Bug, but obvious
}
次に、できるだけ早くそれらから移動します(たとえば、構築リストで)。
非常に長い構成リストがある場合、その中で の 2 つの使用を見逃す可能性がありますがval_moved_from
、これは役に立ちません。
別の方法は、この問題を解決するための提案を作成することです。たとえば、C++ を拡張して、ローカル変数の型またはスコープをそれらの操作によって変更できるようにします。そのため、スコープの残りの部分では、有効期限が切れた変数std::safe_move(X)
から移動しX
、X
使用できなくなります。変数の有効期限が半分になった (あるブランチでは有効期限が切れたが、別のブランチでは有効期限が切れていない) 場合にどうするかは、興味深い問題です。
それは非常識なので、代わりにライブラリの問題として攻撃することができます。ある程度は、実行時にそのようなトリック (型が変化する変数) を偽造することができます。これは大雑把ですが、アイデアは次のとおりです。
template<typename T>
struct read_once : std::tr2::optional<T> {
template<typename U, typename=typename std::enable_if<std::is_convertible<U&&, T>::value>::type>
read_once( U&& u ):std::tr2::optional<T>(std::forward<U>(u)) {}
T move() && {
Assert( *this );
T retval = std::move(**this);
*this = std::tr2::none_t;
return retval;
}
// block operator*?
};
つまり、 via からのみ読み取ることができる線形型を記述し、move
その後はAssert
s または throw を読み取ります。
次に、コンストラクターを変更します。
A::A( read_once<std::string> val ):
_val( val.move() )
{
std::cout << val << std::endl; // does not compile
std::cout << val.move() << std::endl; // compiles, but asserts or throws
}
転送コンストラクターを使用すると、型のないあまりばかげていないインターフェイスを公開してから、コンストラクターを引数のラッパーを使用read_once
して「安全な」(おそらくprivate
) バージョンに転送できます。read_once<>
テストがすべてのコード パスをカバーしている場合、変数から複数回行っても、Assert
単に空の s ではなく、適切な sが取得されます。std::string
move
read_once