16

@Mehrdadによるこの最近の質問を読んだ後、どのクラスを移動不可、したがってコピー不可にする必要があるかについて、コピーできるが移動できないクラスのユースケースがあるかどうか疑問に思い始めました。技術的には、これは可能です。

struct S
{
    S() { }
    S(S const& s) { }
    S(S&&) = delete;
};

S foo()
{
    S s1;
    S s2(s1); // OK (copyable)
    return s1; // ERROR! (non-movable)
}

コピーコンストラクターがSありますが、それは明らかに概念をモデル化していません。これCopyConstructibleは、概念の改良でありMoveConstructible、(削除されていない)移動コンストラクターの存在を必要とするためです(§17.6.3.1 / 2、表21を参照)。 。

S上記のようなタイプのユースケースはありますか?これはコピー可能ですが、 CopyConstructible 移動できませんか?そうでない場合、同じクラスでコピーコンストラクター削除された移動コンストラクターを宣言することが禁止されていないのはなぜですか?

4

5 に答える 5

12

コピーするよりも移動する方が安くないクラスがあるとします(おそらくstd::array、PODタイプのクラスが含まれています)。

機能的には、MoveConstructibleにして、のようにS x = std::move(y);動作するようS x = y;にする必要があります。そのため、CopyConstructibleはMoveConstructibleのサブコンセプトです。通常、コンストラクターをまったく宣言しない場合、これは「正しく機能します」。

実際には、のインスタンスを移動することで、プログラム内に実際よりも効率的に見えるコードがあるかどうかを検出するために、moveコンストラクターを一時的に無効にすることをお勧めします。私にはそれを禁止するのは過度に思えます。完成したコードで優れたインターフェイス設計を実施することは、標準の仕事ではありません:-)S

于 2013-01-14T17:11:05.130 に答える
9

私は現在、削除された移動コンストラクター/代入のユースケースを知りません。不注意に行うと、タイプがファクトリ関数から返されたり、に入れられたりするのを不必要に妨げることになりstd::vectorます。

ただし、削除された移動メンバーは、誰かがそれらの用途を見つける可能性がある場合に備えて、それでも合法です。例えとして、私は何年も役に立たないことを知っていconst&&ました。人々は私たちがそれをただ非合法化するべきではないかと私に尋ねました。しかし、この機能について十分な経験を積んだ後、最終的にいくつかのユースケースが現れました。削除された移動メンバーでも同じことが起こる可能性がありますが、私の知る限りではまだです。

于 2013-01-14T20:12:59.680 に答える
3

移動を防ぎ、コピーを許可する合理的なクラスはないと思います。まったく同じトピックから明らかなように、元のオブジェクトが不要になった場合、移動はコピーの効率的な方法にすぎません。

于 2013-01-14T17:09:54.637 に答える
2

VS2005からVS2010にいくつかのコードを移植し、メモリの破損が発生し始めたため、今日この問題を検討していました。これは、最適化(マップルックアップを実行するときにコピーを回避するため)が移動セマンティクスを使用してC++11に変換されなかったためであることが判明しました。

class CDeepCopy
{
protected:
    char* m_pStr;
    size_t m_length;

    void clone( size_t length, const char* pStr )
    {
        m_length = length;
        m_pStr = new char [m_length+1];
        for ( size_t i = 0; i < length; ++i )
        {
            m_pStr[i] = pStr[i];
        }
        m_pStr[length] = '\0';
    }

public:
    CDeepCopy() : m_pStr( nullptr ), m_length( 0 )
    {
    }

    CDeepCopy( const std::string& str )
    {
        clone( str.length(), str.c_str() );
    }

    CDeepCopy( const CDeepCopy& rhs )
    {
        clone( rhs.m_length, rhs.m_pStr );
    }

    CDeepCopy& operator=( const CDeepCopy& rhs )
    {
        if (this == &rhs)
            return *this;

        clone( rhs.m_length, rhs.m_pStr );
        return *this;
    }

    bool operator<( const CDeepCopy& rhs ) const
    {
        if (m_length < rhs.m_length)
            return true;
        else if (rhs.m_length < m_length)
            return false;

        return strcmp( m_pStr, rhs.m_pStr ) < 0;
    }

    virtual ~CDeepCopy()
    {
        delete [] m_pStr;
    }
};

class CShallowCopy : public CDeepCopy
{
public:

    CShallowCopy( const std::string& str ) : CDeepCopy()
    {
        m_pStr = const_cast<char*>(str.c_str());
        m_length = str.length();
    }

    ~CShallowCopy()
    {
        m_pStr = nullptr;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    std::map<CDeepCopy, int> entries;
    std::string hello( "Hello" );

    CDeepCopy key( hello );
    entries[key] = 1;

    // Named variable - ok
    CShallowCopy key2( hello );
    entries[key2] = 2;

    // Unnamed variable - Oops, calls CDeepCopy( CDeepCopy&& )
    entries[ CShallowCopy( hello ) ] = 3;

    return 0;
}

マップキーがすでに存在する場合に不要なヒープ割り当てを回避したいというコンテキストでした。したがって、CShallowCopyクラスを使用して初期ルックアップを実行し、これが挿入の場合はコピーされます。問題は、このアプローチが移動セマンティクスでは機能しないことです。

于 2013-01-15T17:04:30.580 に答える
0

これは、タイプの移動操作のセマンティクスをどのように定義するかによって異なります。移動が単にリソースの盗用による最適化されたコピーを意味する場合、答えはおそらくノーです。ただし、移動が、移動するガベージコレクタまたはその他のカスタムメモリ管理スキームで使用される意味での「再配置」を意味する場合、答えは「はい」になる可能性があります。

特定の番地にある家の実際の例を考えてみましょう。その家のコピーを、まったく同じ設計図を使用して建てられた別の家であると定義できますが、別の住所にあります。これらの条件の下で、私たちは家をその住所で参照する人がいるかもしれないので、家を動かすことはできないと言い続けることができます。専門用語に変換すると、インバウンドポインタを持つ構造体では移動操作ができない場合があります。

シグナルオブジェクトをコピーすることはできますが、移動することはできない、シグナル/スロットライブラリの少しねじれた実装を想像することができます。

免責事項:一部のC ++純粋主義者は、STL(したがって標準)が移動操作とは何かを定義しており、ここで説明した内容と一致していないため、これについては議論しません。

于 2013-01-15T11:01:27.807 に答える