12

次の例を考えてみましょう。

#include <cstdio>

class object
{
public:
    object()
    {
        printf("constructor\n");
    }

    object(const object &)
    {
        printf("copy constructor\n");
    }

    object(object &&)
    {
        printf("move constructor\n");
    }
};

static object create_object()
{
    object a;
    object b;

    volatile int i = 1;

// With #if 0, object's copy constructor is called; otherwise, its move constructor.
#if 0
    if (i)
        return b; // moves because of the copy elision rules
    else
        return a; // moves because of the copy elision rules
#else
    // Seems equivalent to the above, but behaves differently.
    return i ? b : a; // copies (with g++ 4.7)
#endif
}

int main()
{
    auto data(create_object());

    return 0;
}

そして、C ++ 11ワーキングドラフト、n3337.pdf、12.8 [class.copy]、ポイント32からのこのビットを検討してください。

ソースオブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値で指定されていることを除いて、コピー操作の省略基準が満たされている、または満たされる場合、コピーのコンストラクターを選択するためのオーバーロード解決は次のようになります。オブジェクトが右辺値で指定されているかのように最初に実行されます。オーバーロード解決が失敗した場合、または選択したコンストラクターの最初のパラメーターのタイプがオブジェクトのタイプへの右辺値参照ではない場合(おそらくcv修飾)、オブジェクトを左辺値と見なして、オーバーロード解決が再度実行されます。[注:この2段階の過負荷解決は、コピーの省略が発生するかどうかに関係なく実行する必要があります。省略が実行されない場合に呼び出されるコンストラクターを決定し、呼び出しが省略された場合でも、選択されたコンストラクターにアクセスできる必要があります。—エンドノート]

したがって、この#if 1例で使用する場合、オブジェクトを返すときに最初にmoveコンストラクターが試行され、次にcopyコンストラクターが試行されます。移動コンストラクターがあるため、コピーコンストラクターの代わりに使用されます。

ただし、の最後のreturnステートメントでcreate_object()は、moveコンストラクターが使用されていないことを確認しました。これは言語規則の違反ではありませんか?return言語では、最後のステートメントでmoveコンストラクターを使用する必要がありますか?

4

1 に答える 1

13

条件演算子の仕様が複雑すぎて怖いです。しかし、あなたのコンパイラの動作は正しいと思います。5.16 [expr.cond]/p4 を参照:

2 番目と 3 番目のオペランドが同じ値カテゴリの glvalue で、同じ型の場合、結果はその型と値カテゴリになります ...

12.8 [class.copy]、p31、b1 も参照してください。ここでは、いつコピー省略が許可されるかを説明しています。

クラスの戻り値の型を持つ関数内のreturnステートメントで、式が関数の戻り値の型と同じ cv- 非修飾型を持つ不揮発性自動オブジェクト (関数または catch-clause パラメーター以外) の名前である場合、コピー・移動操作は省略可能 ...

式は自動オブジェクトの名前ではなく、条件式です (その条件式は左辺値です)。したがって、ここではコピーの省略は許可されていません。左辺値式がオーバーロードの解決のために右辺値のふりをすることができると言っているものは他にありません。

于 2012-08-11T15:27:35.687 に答える