10

重複
の可能性: C++11 で関数をデフォルト設定する意味は何ですか?

C++11 では、デフォルトのメソッドが導入されました (例: void myMethod() = default;)。

メソッドに対して何をしますか(メソッドはdefault編集後にどのように動作しますか)。それらを適切に使用するにはどうすればよいですか(その用途は何ですか)?

4

3 に答える 3

4

C++ 標準によって「特別なメンバー関数」と見なされるクラス メンバーが多数あります。これらは:

  • デフォルトのコンストラクター (パラメーターなしで呼び出すことができるコンストラクター)。
  • コピー コンストラクター (左辺値参照としてのオブジェクト型である 1 つのパラメーターで呼び出すことができるコンストラクター)。
  • コピー代入演算子 (左辺値参照または値としてのオブジェクト型である 1 つのパラメーターで呼び出すことができる operator= オーバーロード)。
  • 移動コンストラクター (右辺値参照としてのオブジェクト型である 1 つのパラメーターで呼び出すことができるコンストラクター)。
  • 移動代入演算子 (右辺値参照または値としてのオブジェクト型である 1 つのパラメーターで呼び出すことができる operator= オーバーロード)。
  • デストラクタ。

これらのメンバー関数は、言語が型に対して特別なことを行うという点で特別です。それらを特別なものにするもう1つのことは、コンパイラーが定義を提供しない場合でも提供できることです。= default;これらは、構文を使用できる唯一の関数です。

問題は、コンパイラが特定の条件下でのみ定義を提供することです。つまり、定義が提供されない条件があります。

リスト全体には触れませんが、他の人が言及した例を 1 つ挙げます。特別なコンストラクターではない (つまり、上記のコンストラクターのいずれでもない) 型のコンストラクターを提供する場合、既定のコンストラクターは自動的に生成されません。したがって、このタイプ:

struct NoDefault
{
  NoDefault(float f);
};

NoDefaultデフォルトで構築できません。したがって、デフォルトの構築が必要なコンテキストでは使用できません。NoDefault()一時的なものを作成することはできません。の配列を作成するNoDefaultことはできません(これらはデフォルトで構築されているため)。std::vector<NoDefault>を作成し、コピー元の値を指定せずにサイズ変更コンストラクターを呼び出すことはできませんDefaultConstructible

ただし、これを行うことができます:

struct UserDefault
{
  UserDefault() {}
  UserDefault(float f);
};

それはすべてを修正しますよね?

違う!

それはこれと同じではありません:

struct StdDefault
{
  StdDefault() = default;
  StdDefault(float f);
};

なんで?平凡なStdDefaultタイプだから。どういう意味ですか?すべてを説明することはしませんが、詳細についてはこちらを参照してください。型を自明にすることは、可能な場合に便利な機能であることがよくあります。

自明な型の要件の 1 つは、ユーザー提供の既定のコンストラクターがないことです。UserDefaultコンパイラによって生成されたものとまったく同じことを行いますが、提供されたものがあります。したがって、UserDefault自明ではありません。構文はコンパイラがそれを生成することを意味するStdDefaultため、自明な型です。= defaultしたがって、ユーザー提供ではありません。

この= default構文は、「通常は生成しない場合でも、とにかくこの関数を生成する」ことをコンパイラに伝えます。コンパイラのように特別なメンバーを実際に実装することはできないため、これはクラスが自明な型であることを確認するために重要です。これにより、コンパイラーが関数を生成しない場合でも強制的に生成することができます。

これは、多くの状況で非常に役立ちます。例えば:

class UniqueThing
{
public:
  UniqueThing() : m_ptr(new SomeType()) {}
  UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
  UniqueThing &operator =(const UniqueThing &ptr)
  {
    m_ptr.reset(new SomeType(*ptr)); return *this;
  }

private:
  std::unique_ptr<SomeType> m_ptr;
};

クラスに unique_ptr の内容をコピーさせるには、これらの関数をすべて記述する必要があります。それを避ける方法はありません。しかし、これも移動可能にしたいので、移動コンストラクター/代入演算子は自動的に生成されません。それらを再実装するのはばかげているので (さらにメンバーを追加するとエラーが発生しやすくなります)、次の= default構文を使用できます。

class UniqueThing
{
public:
  UniqueThing() : m_ptr(new SomeType()) {}
  UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
  UniqueThing(UniqueThing &&ptr) = default;
  UniqueThing &operator =(const UniqueThing &ptr)
  {
    m_ptr.reset(new SomeType(*ptr)); return *this;
  }

  UniqueThing &operator =(UniqueThing &&ptr) = default;

private:
  std::unique_ptr<SomeType> m_ptr;
};
于 2012-12-01T02:38:11.677 に答える
3

コンストラクターまたはコピー コンストラクターで使用= defaultするということは、コンパイラーがコンパイル時にその関数を生成することを意味します。これは通常、class/からそれぞれの関数を除外すると発生しますstruct。また、独自のコンストラクターを定義する場合、この新しい構文を使用すると、通常はコンストラクターをデフォルトで構築しないようにコンパイラーに明示的に指示できます。たとえば、次のようにします。

struct S {

};

int main() {

    S s1; // 1
    S s2(s1); // 2

}

コンパイラはコピー コンストラクターとデフォルト コンストラクターの両方を生成するため、S(1) のようにオブジェクトをインスタンス化し、(2) にコピーs2することができますs1。しかし、独自のコンストラクターを定義すると、同じ方法でインスタンス化できないことがわかります。

struct S {
    S(int);
};

int main() {

    S s;    // illegal
    S s(5); // legal

}

カスタム定義のコンストラクターが指定されているため、これは失敗しS、コンパイラーはデフォルトのコンストラクターを生成しません。しかし、それでもコンパイラに提供してもらいたい場合は、次を使用して明示的に伝えることができdefaultます。

struct S {
    S(int);
    S() = default;
    S(const S &) = default;
};

int main() {

    S s;     // legal
    S s(5);  // legal

    S s2(s); // legal

}

ただし、特定の関数シグネチャを持つ特定の関数のみをオーバーロードできることに注意してください。これは、コピー コンストラクターでもデフォルト コンストラクターでも移動演算子でも代入演算子でもないため、失敗します。

struct S {
    S(int) = default; // error
};

代入演算子を明示的に定義することもできます (default/copy-constructor と同様に、独自のものを書き出さない場合、デフォルトの演算子が生成されます)。たとえば、次のようにします。

struct S {
    int operator=(int) {
        // do our own stuff
    }
};

int main() {

    S s;

    s = 4;

}

Sこれはコンパイルされますが、別のオブジェクトをに割り当てたい一般的なケースでは機能しませんs。これは、独自に作成したため、コンパイラが提供しないためです。ここでdefault出番です:

struct S {
    int operator=(int) {
        // do our own stuff
    }

    S & operator=(const S &) = default;
};

int main() {

    S s1, s2;

    s1 = s2; // legal

}

ただし、技術的には、代入演算子、コピー、または移動コンストラクターをデフォルトとして定義するのは不必要です。それを「オーバーライドする」関数が他にない場合、その場合はコンパイラーが提供するためです。提供されない唯一のケースは、独自のもの (またはそのバリエーション) を定義する場合です。ただし、好みの問題として、前述の新しい構文を使用してデフォルト関数を提供するようコンパイラーに要求することは、コンパイラーに対して意図的に省略されている場合、より明示的で見やすく理解しやすいものです。

于 2012-12-01T01:09:15.817 に答える