2

次のコードを検討してください。

class Foo {
 public:
  explicit Foo(double) {}
};

Foo * test();
Foo * test() {
  return new Foo(Foo(1.0));   // (1)
}

私の質問は行 (1) に関するものです。これは、追跡に時間がかかったバグと非常によく似ています。コピー/貼り付けエラーにより、型が 2 回指定されていることに気付きませんでした。正しい行は明らかに次のとおりです。

  return new Foo(1.0);

興味深いことに、この変更も警告なしでコンパイルされるようです。

  return new Foo(Foo(Foo(Foo(1.0))));

-Wall -Weverythingこれらの例は、フラグを使用しても、clang で警告なしにコンパイルされるのはなぜですか? Foo のインスタンスを有効な引数Foo::Foo(double)として受け入れるのはなぜですか? doubleこれは、新しい演算子の特異な動作ですか?

私の元のコードはより大きなコンテキストにあり、2 つの LLVM-3 ベースのコンパイラでテストされました。どちらも警告やエラーなしでコンパイルされました。1 つでは、コードは実際に期待どおりに機能し、実際、バグがあることにしばらく気づきませんでした。もう 1 つの例では、Foo のインスタンスが非常に奇妙に動作しました。正確に説明することはできませんが、返されたポインターの後のコピーが「魔法のように」元の値とは異なる値になり、2 つの協調動作の間で不一致の状態が発生したかのようでした。共有 Foo への同等のポインターを保持するはずだったオブジェクトが、何らかの理由で割り当て後に異なる値を保持していました。ここで何が起こっているのかを理解するまでは、本当に奇妙に思えます!

興味深いことに、以下は両方のコンパイラでコンパイルされます。

class Foo { public: explicit Foo(double) {} };
class Bar { public: explicit Bar(double) {} };

Foo * testFoo() { return new Foo(Foo(1.0)); }
Bar * testBar() { return new Bar(Bar(1.0)); }

しかし、次のバージョンはそうではありません:

Foo * testFooBar() { return new Foo(Bar(1.0)); }
4

2 に答える 2

7

コンパイラは自動的にコピー コンストラクターを生成し、Foo(const Foo&)正確なバージョン/設定に応じてコンストラクターを移動することもありますFoo(Foo&&)。これは、演算子 new やポインター マジックとは関係ありません。コードは、コンパイラによって定義された完全に通常のコピー/移動コンストラクタを呼び出すだけです。それで全部です。この動作は標準で義務付けられています。コピー不可能なクラス (少なくとも標準の元のバージョンでは) は実質的に価値がありません。

自動生成されたコピー コンストラクターが必要ない場合、通常の手法は、それらを削除済みまたは非公開として定義することです。

また、コンパイラは、状況によっては、通常はそうすべきではないのに、プログラムからオブジェクト全体を実際に削除する権利を持っていることにも注意してください。Foo コンストラクターで Foo へのポインターを設定してハイジンクを行う場合、すべてのコンストラクターとデストラクタでそれらを厳密に処理する必要があります。つまり、独自のコピー/移動コンストラクター、デストラクタ、代入演算子を作成する必要があります。コンパイラがオブジェクトを省略したときのクロッパー。

于 2014-08-02T10:55:05.757 に答える
2

観察しているコンストラクターはコピーコンストラクターです。すべてのクラスにはコピー コンストラクターがあります。クラス定義でコピー コンストラクターを宣言しない場合は、コピー コンストラクターが暗黙的に宣言されます。

暗黙的に宣言されたコピー コンストラクターの特定のシグネチャは、クラス定義、つまりベースとメンバーに依存します。通常は の形式ですがX::X(X const &)、必要に応じてX::X(X &)またはのX::X(X volatile &)場合もあります。例外仕様は可能な限り厳密です。暗黙的に宣言されたコピー コンストラクターの定義は、通常、ベースとメンバーのコピーで構成されますが、特定の状況 (移動コンストラクターを宣言する場合など) では削除されると定義される場合があります。

(C++ の将来のバージョンでは、コピーまたは移動代入演算子を宣言すると、暗黙的に宣言されたコピー コンストラクターが削除されたものとして定義されます。C++11 および C++14 では、既定値になります。)

于 2014-08-02T11:19:29.697 に答える