51

C ++ 0xの右辺値に関する主なアドバイスは、コンパイラーがデフォルトで実装するまで、moveコンストラクターとmove演算子をクラスに追加することです。

ただし、VC10を使用する場合、待機は失われる戦略です。自動生成は、VC10 SP1、または最悪の場合はVC11まではおそらくここにないからです。おそらく、これを待つ時間は数年で測定されます。

ここに私の問題があります。この重複したコードをすべて書くのは楽しいことではありません。そして、見るのは不快です。しかし、これは、遅いと見なされるクラスにとって、好評の負担です。数千とまではいかなくても、数百の小さなクラスではそうではありません。

::ため息::C++ 0xを使用すると、コードの記述量を増やすことはできません

そして、私は考えました。多くの人が共有していると思います。

すべてを値で渡すだけではどうでしょうか。std :: move + copy elisionはこれをほぼ最適にしませんか?

例1-典型的なPre-0xコンストラクター

OurClass::OurClass(const SomeClass& obj) : obj(obj) {}

SomeClass o;
OurClass(o);            // single copy
OurClass(std::move(o)); // single copy
OurClass(SomeClass());  // single copy

短所:右辺値の無駄なコピー。

例2-推奨されるC++0x?

OurClass::OurClass(const SomeClass& obj) : obj(obj) {}
OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}

SomeClass o;
OurClass(o);            // single copy
OurClass(std::move(o)); // zero copies, one move
OurClass(SomeClass());  // zero copies, one move

長所:おそらく最速です。
短所:たくさんのコード!

例3-値渡し+std:: move

OurClass::OurClass(SomeClass obj) : obj(std::move(obj)) {}

SomeClass o;
OurClass(o);            // single copy, one move
OurClass(std::move(o)); // zero copies, two moves
OurClass(SomeClass());  // zero copies, one move

長所:追加のコードはありません。
短所:ケース1と2での無駄な移動SomeClass。移動コンストラクターがないと、パフォーマンスが大幅に低下します。


どう思いますか?これは正しいです?コード削減のメリットと比較した場合、発生した移動は一般的に許容できる損失ですか?

4

1 に答える 1

8

私はこのトピックに不慣れで、いくつかの調査を行ったので、あなたの質問に興味がありました。結果を紹介します。

まず、ため息をつきます。

::ため息::C++ 0xを使用すると、コードの記述量を増やすことはできません。

また、コードをより適切に制御できるようにすることも想定されています。そしてそれはそうです。私は追加のコンストラクターに固執します:

OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}

個人的には、複雑で重要な状況での冗長性を好みます。冗長性により、私とコードの読者の可能性に注意を向けることができるからです。

たとえば、Cスタイルのキャスト(T*)pTとC ++標準を使用static_cast<T*>(pT) します。はるかに冗長ですが、大きな前進です。

第二に、私はあなたの例3、最後のテストケースについて少し疑っていました。右辺値から値渡しパラメーターを作成するために、別の移動コンストラクターが関係している可能性があると思いました。だから私は私の新しいVS2010でいくつかの簡単なプロジェクトを作成し、いくつかの説明を得ました。ここにコードと結果を投稿します。

起源:

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <utility>
#include <iostream>

class SomeClass{
    mutable int *pVal;
public:
    int Val() const { return *pVal; };
    SomeClass(int val){
        pVal = new int(val);
        std::cout << "SomeClass constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
    SomeClass(const SomeClass& r){
        pVal = new int(r.Val());
        std::cout << "SomeClass copy constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }   
    SomeClass(const SomeClass&& r){
        pVal = r.pVal;
        r.pVal = 0;
        std::cout << "SomeClass move constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
    ~SomeClass(){
        if(pVal)
            delete pVal;
        std::cout << "SomeClass destructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
};

class OtherClass{
    SomeClass sc;
public:
    OtherClass(int val):sc(val){
    }

このセクションに注意してください:

#if 1
    OtherClass(SomeClass r):sc(std::move(r)){
    }
#else
    OtherClass(const SomeClass& r):sc(r){
    }   
    OtherClass(const SomeClass&& r):sc(std::move(r)){
    }
#endif

..。

    int Val(){ return sc.Val(); }
    ~OtherClass(){
    }
};

#define ECHO(expr)  std::cout << std::endl << "line " << __LINE__ << ":\t" #expr ":" << std::endl; expr

int _tmain(int argc, _TCHAR* argv[])
{
    volatile int __dummy = 0;
    ECHO(SomeClass o(10));

    ECHO(OtherClass oo1(o));            
    __dummy += oo1.Val();
    ECHO(OtherClass oo2(std::move(o))); 
    __dummy += oo2.Val();
    ECHO(OtherClass oo3(SomeClass(20)));  
    __dummy += oo3.Val();

    ECHO(std::cout << __dummy << std::endl);
    ECHO(return 0);
}

お気づきのとおり、2つのアプローチをテストできるコンパイル時スイッチがあります。

結果はテキスト比較モードで最もよく表示されます。左側に#if 1コンパイルが表示され、提案された回避策を確認します。右側に- #if 0、c++0xで説明されている「kosher」の方法を確認します。

私はコンパイラが愚かなことをするのではないかと疑っていました。3番目のテストケースで余分なmoveコンストラクターを節約しました。

正直なところ、提案された回避策で呼び出された別の2つのデストラクタを考慮する必要がありますが、これは、破壊されているオブジェクトで移動が発生した場合にアクションを実行してはならないことを考慮すると、確かに小さな欠点です。それでも、知っておくのは良いことです。

いずれにせよ、ラッパークラスにもう1つのコンストラクターを作成する方がよいという点を残していません。面倒な作業はすべて、 moveコンストラクターが必要なですでに行われSomeClassいるため、これは数行の問題です。

于 2010-06-09T18:57:54.413 に答える