2013-07-03 11 views
5

C++ 11に移行したので、私の文字列を私のコンストラクタで体系的に値渡ししています。しかし、今、私は、コンストラクタの本体で値を使用しているときにも、それはそれが簡単にバグを導入することができますことを実現:誤って移動した値を使用する

class A(std::string val): 
    _val(std::move(val)) 
{ 
    std::cout << val << std::endl; // Bug!!! 
} 

私はそれが間違って得ることの可能性を減らすために何ができますか?その目的は

+7

は非常にいい –

+0

「C++ 11に_move_を作った」「私はそれが間違って得ることの可能性を減らすために何ができるの?」あまりない。オブジェクトを有効ではあるが奇妙な状態にしておくのはばかげている。これはC++ 11の移動セマンティクスがばかげている理由の1つです。 –

+0

コンストラクタの本体内で_valを使用するとどうなりますか? – Thorsten

答えて

2

名の引数は、その後できるだけ早いそこから移動移動-からするために、いくつかの特徴的な方法で、少なくともコンストラクタ

A::A(std::string val_moved_from): 
_val(std::move(val_moved_from)) 
{ 
    std::cout << val_moved_from << std::endl; // Bug, but obvious 
} 

の実装内である(建設リストで、言います) 。

このような長い構造リストをお持ちの場合は、val_moved_fromの2つの用途が不足する可能性がありますが、これは役に立ちません。

この問題を解決するための提案を書くこともできます。 C++を拡張して、ローカル変数の型やスコープを操作で変更できるようにします。std::safe_move(X)Xから移動し、有効範囲外の有効期限切れの変数としてXとマークします。変数が半分期限切れになったとき(1つのブランチで期限切れになっているが、別のブランチでは期限切れになっている)に何をすべきかは、興味深い質問です。

これは非常識なので、代わりにライブラリの問題として攻撃することができます。ある限られた範囲では、実行時にそのような種類のトリック(型が変わる変数)を偽造することができます。これは、原油ですが、アイデアを提供します:

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*? 
}; 

にすなわち、唯一moveを経由してから読み取ることができるリニア型を書き、その時間の後Assert秒を読んだり、スローされます。

その後、あなたのコンストラクタを変更します。フォワーディングコンストラクタと

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タイプを使用してコンストラクタをあまりばかげインターフェイスを公開することができます議論。

あなたのテストは、すべてのコードパスをカバーしている場合、あなたはあなたのread_once変数から行くと何度もmoveよりも、だけではなく、空のstd::string秒の素敵なAssert秒を取得します。

+2

しかし、あなたはこれをすべて忘れてはいけません。あなたがそれを覚えていれば、まずは間違いを犯さなかったでしょう。 –

+0

@LightnessRacesinOrbit確かに、堅牢な 'read_once'を持っていて、' 'read_once'"としてコピーする値で常に "取る"というルールに従えば、それを読むときに空のデータではなく 'Assert'を得ます。ホイッスルはトラを守っていますが、トラは見えますか? – Yakk

+0

あなたが "間違った変数を印刷しようとしないでください"というルールに従えば、それはすべてだと思います。 –

0

"C++ 11に移行したので、私の文字列をコンストラクタに体系的に渡しています。"

申し訳ありませんが、私はなぜこれをやりたいのか分かりません。伝統的な方法と比較して、これはどのような改善をもたらしますか? (これは基本的にバグの証拠です)。

 
class A(const std::string & s): 
    _val(s) 
{ 
    std::cout << s << std::endl; // no Bug!!! 
    std::cout << _val << std::endl; // no Bug either !!! 
} 

+0

質問に答えるには:yes、 'std :: move'はこれより優れています。さらに、あなたの答えは質問に答えません。 – milleniumbug

+0

's'が100,000文字の文字列であるとします。あなたのバージョンは常に、それをコピーします。常に、ここでは '_val(s)'です。上記のバージョンでは、呼び出し元がコンストラクタに 'std :: move'を実行したり、一時的な' std :: string'から 'A'を構築したりすると、そのコピーは作成されません。詳細は、[here](http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/)を参照してください。 'std :: string && s'と' std :: string const&s'コンストラクタの両方を使うと、 'std :: string s'を取るよりも動きが少なくなりますが、維持するコードの量は2倍になります。 – Yakk

+0

この例では、参照渡しとしてコピーを作成していません。私は引用された議論に非常に精通しています。ここにはないコピーがある状況に対処しています。問題は、「間違ってしまう可能性を減らすために何ができるのか」です。答えは「しないでください!」です。これを行うことでコピーを減らさず、バグを導入しています。 –

関連する問題