5

これはr値の実験でしたが、gccがmove-constructorの欠如について私に泣き言を言ったときに変異し(私はそれを削除しました)、コピーコンストラクターにフォールバックしませんでした(予想どおり)-std =を削除しましたフラグからc++ 11を実行し、以下に示すものを試してみました.出力がたくさんあります(最初はそうではありませんでした)。 stdout のメッセージは、何かが起こっていることを示す良い指標となります)

これが私のコードです:

#include <iostream>

class Object {
public:
    Object() { id=nextId; std::cout << "Creating object: "<<id<<"\n"; nextId++; }
    Object(const Object& from) {
         id=nextId; std::cout << "Creating object: "<<id<<"\n"; nextId++;
        std::cout<<"(Object: "<<id<<" created from Object: "<<from.id<<")\n";
    }
    Object& operator=(const Object& from) {
        std::cout<<"Assigning to "<<id<<" from "<<from.id<<"\n";
        return *this;
    }
    ~Object() { std::cout<<"Deconstructing object: "<<id<<"\n";}

private:
    static int nextId;
    int id;
};

int Object::nextId = 0;

Object test();

int main(int,char**) {
    Object a;
    std::cout<<"A ought to exist\n";
    Object b(test());
    std::cout<<"B ought to exist\n";
    Object c = test();
    std::cout<<"C ought to exist\n";
    return 0;
}


Object test() {
    std::cout<<"In test\n";
    Object tmp;
    std::cout<<"Test's tmp ought to exist\n";
    return tmp;
}

出力:

Creating object: 0
A ought to exist
In test
Creating object: 1
Test's tmp ought to exist
B ought to exist
In test
Creating object: 2
Test's tmp ought to exist
C ought to exist
Deconstructing object: 2
Deconstructing object: 1
Deconstructing object: 0

私は deconstructing を使用します。なぜなら、deconstructing はすでに単語になっているからです。時々私は destructor を使用します。私はこの単語にあまり満足していません。名詞として destructor を好みます。

これが私が期待したものです:

A to be constructed
tmp in test to be constructed, a temporary to be created from that 
    tmp, tmp to be destructed(?) 
that temporary to be the argument to B's copy constructor
the temporary to be destructed.
C's default constructor to be used
"" with a temporary from `test`
C's assignment operator to be used
the temporary to be destructed
c,b,a to be destructed.

私は「頑固な C」と呼ばれており、C++ を「名前空間を持つ C」以上のものとして使用することを学ぼうとしています。

誰かが「コンパイラが最適化する」と言うかもしれません。そのような人には、そのような答えで質問に答えないでほしいと思います.最適化はプログラムの状態を変更してはなりませ.コンパイラは、数値を含む cout にメッセージを表示することで私をからかうかもしれません。数値を増やすことさえ気にしないかもしれませんが、プログラムの出力は、コードが記述するすべてのことを実行した場合と同じになります。

これは最適化ではありません。何が起こっているのでしょうか?

4

4 に答える 4

5

これは最適化であり、プログラムの観察可能な動作を変更できる唯一の最適化です。

12.8./31標準ドラフトn3337(強調鉱山)から取られた段落は次のとおりです。

特定の基準が満たされると、オブジェクトのコピー/移動コンストラクターおよび/またはデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。このような場合、実装は、省略されたコピー/移動操作のソースとターゲットを、同じオブジェクトを参照する 2 つの異なる方法として扱い、そのオブジェクトの破棄は、2 つのオブジェクトが削除されていた時間のうちの遅い方の時点で発生します。最適化なしで破壊されました。コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます (複数のコピーを排除するために組み合わせることができます)。

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

— throw 式で、オペランドが非 volatile 自動オブジェクト (関数または catch 句パラメーター以外) の名前であり、そのスコープが最も内側の try ブロックの末尾を超えて拡張されていない場合 (存在する場合) 1 つ)、オペランドから例外オブジェクト (15.1) へのコピー/移動操作は、自動オブジェクトを直接例外オブジェクトに構築することによって省略できます。

— 参照 (12.2) にバインドされていない一時クラス オブジェクトが同じ cv 非修飾型のクラス オブジェクトにコピー/移動される場合、一時オブジェクトを省略したコピー/移動の対象

— 例外ハンドラーの例外宣言 (条項 15) が、例外オブジェクト (15.1) と同じタイプ (cv 修飾を除く) のオブジェクトを宣言する場合、コピー/移動操作は、例外宣言を処理することによって省略できます。 exception-declaration によって宣言されたオブジェクトのコンストラクタとデストラクタの実行を除いて、プログラムの意味が変更されない場合は、例外オブジェクトのエイリアスとして。

【例…略】

コピー/移動コンストラクターのセマンティクスは、別のオブジェクトを初期化しながら、オブジェクトのコンテンツをコピー/移動することです。あなたのコピー コンストラクターがあなたの誕生日パーティーへの招待状を電子メールで送信したとしても、あなたが一人でパーティーをすることになっても驚かないでください :)

OK、一部のコピー コンストラクターは他のことも行います。スマート ポインターの参照カウントを考えてみてください。しかし、それが最適化されれば問題ありません。コピーはなく、数える必要もありませんでした。

于 2013-08-28T11:46:51.523 に答える
3

一時オブジェクトのコピー/移動にはコストがかかるため、対応するコンストラクタまたはデストラクタに副作用がある場合でも、コンパイラは一時オブジェクトを明示的に除外できます。通常、コピー/移動省略は最適化とは見なされず、ほとんどのコンパイラは、デバッグ モードでも一時オブジェクトの構築を省略します (デバッグ ビルドと最適化ビルドで異なる動作をさせたくないため、これは合理的です)。

C++11 標準の関連節は 12.8 [class.copy] パラグラフ 31 です。

特定の基準が満たされると、コピー/移動操作用に選択されたコンストラクターおよび/またはオブジェクトのデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。...

コピー/移動の省略が許可されるケースは次のとおりです。

  • リターンステートメントで
  • throw式で
  • 一時が参照にバインドされていない場合
  • catch節で

正確なルールには、いくつかの追加条件があります。

于 2013-08-28T11:51:08.343 に答える
0

コピーの不正を防ぐには、次のようなコピー/スワップ アルゴリズムを使用して代入演算子を実装します。

Object &operator =(Object other)
{
    std::swap(*this, other);
    return *this;
}

そして、試してください:

Object a;
a = test();

こうすることで、オブジェクトを代入演算子に渡すときに、コピー (または移動) ctor がコンパイラによって呼び出されます。

于 2013-08-28T11:50:32.427 に答える