C ++ 11の新しい構文「= default」


136

私はなぜこれをするのか理解できません:

struct S { 
    int a; 
    S(int aa) : a(aa) {} 
    S() = default; 
};

なぜただ言ってはいけない:

S() {} // instead of S() = default;

なぜそのための新しい構文を導入するのですか?


30
Nitpick:defaultは新しいキーワードではなく、すでに予約済みのキーワードの新しい使用法にすぎません。


ことMEY この質問はあなたを助けるかもしれません。
FreeNickname 2013

7
他の答えに加えて、「=デフォルト」も主張します。より自己文書化されています。
マーク

回答:


136

デフォルトのデフォルトコンストラクターは、初期化リストと空の複合ステートメントを持たないユーザー定義のデフォルトコンストラクターと同じであると明確に定義されています。

§12.1/ 6 [class.ctor]デフォルトで削除済みとして定義されていないデフォルトのコンストラクターは、クラス型のオブジェクトを作成するためにodrを使用するとき、または最初の宣言の後に明示的にデフォルトになるときに暗黙的に定義されます。暗黙的に定義されたデフォルトコンストラクターは、クラスの初期化セットを実行します。これは、ctor-initializer(12.6.2)および空の複合ステートメントなしで、そのクラスのユーザー作成のデフォルトコンストラクターによって実行されます。[...]

ただし、両方のコンストラクタは同じように動作しますが、空の実装を提供すると、クラスの一部のプロパティに影響します。ユーザー定義コンストラクターを指定すると、何も実行されない場合でも、型が集計ではなく、簡単ではなくなります。クラスを集約型または自明な型(または推移性、POD型)にする場合は、を使用する必要があります= default

§8.5.1/ 1 [dcl.init.aggr]集合体は、ユーザー提供のコンストラクタを持たない配列またはクラスです[および...]

§12.1/ 5 [class.ctor]デフォルトのコンストラクタは、ユーザーが指定せず、[...]

§9/ 6 [クラス]トリビアルクラスは、トリビアルデフォルトコンストラクターと[...]を持つクラスです。

実証するには:

#include <type_traits>

struct X {
    X() = default;
};

struct Y {
    Y() { };
};

int main() {
    static_assert(std::is_trivial<X>::value, "X should be trivial");
    static_assert(std::is_pod<X>::value, "X should be POD");
    
    static_assert(!std::is_trivial<Y>::value, "Y should not be trivial");
    static_assert(!std::is_pod<Y>::value, "Y should not be POD");
}

さらに、明示的なデフォルトのコンストラクターはconstexpr、暗黙のコンストラクターがそうであった場合にそれを作成し、暗黙のコンストラクターが持っていたのと同じ例外仕様もそれに与えます。あなたが与えた場合、暗黙のコンストラクターはconstexpr(データメンバーを初期化しないままにするため)されておらず、空の例外指定もあるため、違いはありません。しかし、はい、一般的なケースconstexprでは、暗黙のコンストラクターと一致するように、手動で例外仕様を指定できます。

= defaultコピー/移動コンストラクタとデストラクタでも使用できるため、を使用するとある程度の均一性が得られます。たとえば、空のコピーコンストラクターは、既定のコピーコンストラクター(メンバーのメンバーごとのコピーを実行します)と同じことは行いません。これらの特別なメンバー関数ごとに= default(または= delete)構文を均一に使用すると、意図を明示的に示すことでコードが読みやすくなります。


ほとんど。12.1 / 6:「ユーザー作成のデフォルトコンストラクターがコンストラクターの要件constexpr(7.1.5)を満たす場合、暗黙的に定義されたデフォルトコンストラクターはconstexprです。」
ケーシー

実際、8.4.2 / 2はより有益です。「関数が最初の宣言で明示的にデフォルトになっているconstexpr場合、(a)暗黙の宣言がそうである場合は暗黙的に見なされます。(b)同じであると暗黙的に見なされます。暗黙的に宣言されたかのように例外仕様(15.4)、... "この特定のケースでは違いはありませんが、一般にfoo() = default;に比べてわずかに有利foo() {}です。
ケーシー

2
違いはないと言い、違いを説明しますか?

@hvdこの場合、constexpr(データメンバーが初期化されていないため)暗黙の宣言は行われず、その例外指定はすべての例外を許可するため、違いはありません。私はそれをより明確にします。
Joseph Mansfield

2
説明をありがとう。まだで、しかし、違いがあるように思えるんconstexpr(これはあなたがここでの違いを作るべきではありません言及した):struct S1 { int m; S1() {} S1(int m) : m(m) {} }; struct S2 { int m; S2() = default; S2(int m) : m(m) {} }; constexpr S1 s1 {}; constexpr S2 s2 {};だけはs1、エラーを与えませんs2。clangとg ++の両方。

10

私は違いを示す例があります:

#include <iostream>

using namespace std;
class A 
{
public:
    int x;
    A(){}
};

class B 
{
public:
    int x;
    B()=default;
};


int main() 
{ 
    int x = 5;
    new(&x)A(); // Call for empty constructor, which does nothing
    cout << x << endl;
    new(&x)B; // Call for default constructor
    cout << x << endl;
    new(&x)B(); // Call for default constructor + Value initialization
    cout << x << endl;
    return 0; 
} 

出力:

5
5
0

ご覧のとおり、空のA()コンストラクターの呼び出しはメンバーを初期化しませんが、B()は初期化します。


7
この構文について説明してください-> new(&x)A();
Vencat

5
(新しいメモリ割り当てではなく)変数xのアドレスから始まるメモリに新しいオブジェクトを作成しています。この構文は、事前に割り当てられたメモリにオブジェクトを作成するために使用されます。私たちの場合のように、Bのサイズ= intのサイズなので、new(&x)A()はx変数の代わりに新しいオブジェクトを作成します。
Slavenskij

ご説明ありがとうございます。
Vencat、

1
gcc 8.3で異なる結果が得られます:ideone.com/XouXux
Adam.Er8

C ++ 14でも、異なる結果が得られます。ideone.com
Mayank Bhushan

9

n2210にはいくつかの理由があります。

デフォルトの管理にはいくつかの問題があります。

  • コンストラクター定義は結合されています。コンストラクターを宣言すると、デフォルトのコンストラクターが抑制されます。
  • デストラクタのデフォルトは、ポリモーフィッククラスには不適切であり、明示的な定義が必要です。
  • デフォルトが抑制されると、デフォルトを復活させる方法はありません。
  • 多くの場合、デフォルトの実装は手動で指定した実装よりも効率的です。
  • デフォルト以外の実装は重要なものであり、タイプのセマンティクスに影響します。たとえば、タイプを非PODにします。
  • (自明ではない)置換を宣言せずに、特別なメンバー関数またはグローバル演算子を禁止する手段はありません。

type::type() = default;
type::type() { x = 3; }

追加のメンバーの宣言によりデフォルトが変更されるため、場合によっては、クラス関数はメンバー関数の定義を変更せずに変更できます。

参照ルール・オブ・スリーは、C ++ 11とのルール・オブ・ファイブとなって?

他の特別なメンバー関数を明示的に宣言するクラスでは、移動コンストラクターと移動割り当て演算子は生成されません。移動コンストラクターまたは移動を明示的に宣言するクラスでは、コピーコンストラクターとコピー割り当て演算子は生成されません。代入演算子、および明示的に宣言されたデストラクタと暗黙的に定義されたコピーコンストラクターまたは暗黙的に定義されたコピー代入演算子を持つクラスは非推奨と見なされます


1
これらは、コンストラクタ= defaultで実行= defaultするのではなく、一般的に実行する理由です{ }
ジョセフマンスフィールド

@JosephMansfield真ですが、{}が導入される前はすでに言語の機能だったため=default、これらの理由暗黙的に区別に依存しています(たとえば、[[抑制されたデフォルト]を復活させる手段{}ない)とは、デフォルトと同等ではないことを意味します)。
カイルストランド2015年

7

場合によっては、セマンティクスの問題です。デフォルトのコンストラクターではそれほど明白ではありませんが、他のコンパイラー生成メンバー関数では明白になります。

デフォルトのコンストラクターの場合、空のボディを持つデフォルトのコンストラクターを、を使用する場合と同じように、自明なコンストラクターの候補と見なすことができます=default。結局のところ、古い空のデフォルトコンストラクターは正当なC ++でした。

struct S { 
  int a; 
  S() {} // legal C++ 
};

コンパイラーがこのコンストラクターを自明であると理解するかどうかは、最適化(手動またはコンパイラーの最適化)の外ではほとんどの場合関係ありません。

ただし、空の関数本体を「デフォルト」として扱うこの試みは、他のタイプのメンバー関数では完全に機能しなくなります。コピーコンストラクタを考えてみましょう:

struct S { 
  int a; 
  S() {}
  S(const S&) {} // legal, but semantically wrong
};

上記の場合、空のボディで記述されたコピーコンストラクターは間違っています。実際には何もコピーしていません。これは、デフォルトのコピーコンストラクタのセマンティクスとはまったく異なるセマンティクスのセットです。望ましい動作を実現するには、いくつかのコードを記述する必要があります。

struct S { 
  int a; 
  S() {}
  S(const S& src) : a(src.a) {} // fixed
};

ただし、この単純なケースでも、コピーコンストラクターがそれ自体が生成するものと同一であることを確認したり、コピーコンストラクターが自明であることを確認したりすることは、コンパイラーにとってはるかに負担になります(memcpy基本的に、 )。コンパイラーは、各メンバー初期化子式をチェックして、ソースの対応するメンバーにアクセスするために式と同一であることを確認する必要があります。また、重要なデフォルトの構造を持つメンバーが残っていないことを確認する必要があります。コンパイラーは、この関数の生成されたバージョンが自明であることを確認するために使用します。

次に、特に自明ではない場合に、さらに複雑になる可能性があるコピー割り当て演算子を検討します。これは、多くのクラスのために記述したくない大量の定型文ですが、C ++ 03ではとにかく強制されます。

struct T { 
  std::shared_ptr<int> b; 
  T(); // the usual definitions
  T(const T&);
  T& operator=(const T& src) {
    if (this != &src) // not actually needed for this simple example
      b = src.b; // non-trivial operation
    return *this;
};

これは単純なケースですが、このような単純な型に対してT(特に、操作をミックスに移動すると)強制的に作成する必要があるよりも多くのコードになっています。空のボディはすでに完全に有効であり、明確な意味があるため、「デフォルトを埋める」という意味の空のボディに依存することはできません。実際、空のボディが「デフォルトを埋める」ことを示すために使用された場合、何もしないコピーコンストラクタなどを明示的に作成する方法はありません。

これも一貫性の問題です。空のボディは「何もしない」を意味しますが、コピーコンストラクターのようなものでは、「何もしない」ことを本当に望まず、むしろ「抑制されない場合に通常行うすべてのことを行います」したがって=default。それはだ、必要なコピー/移動コンストラクタと代入演算子のような抑制コンパイラで生成されたメンバ関数を克服するために。その場合、デフォルトのコンストラクターでも機能するように「自明」です。

空のボディと単純なメンバー/ベースコンストラクターを持つデフォルトのコンストラクターも=default、古いコードをより最適化する場合と同じように、単純なものと見なされるのは良いことかもしれませんが、ほとんどの低レベルのコードは単純なものに依存しています最適化のデフォルトコンストラクターも、単純なコピーコンストラクターに依存しています。古いコピーコンストラクターをすべて「修正」する必要がある場合は、古いデフォルトコンストラクターもすべて修正する必要はありません。また、明示=defaultを使用して意図を示すことで、より明確で明白になります。

コンパイラによって生成されたメンバー関数が実行する他のいくつかのことも、サポートに明示的に変更を加える必要があります。constexprデフォルトのコンストラクタのサポートはその一例です。=default他のすべての特別なキーワードなど=default、C ++ 11の暗黙のテーマである関数をマークアップするよりも、精神的に使用する方が簡単です。つまり、言語をより簡単にします。それはまだたくさんのいぼと逆互換性の妥協を得ていますが、使いやすさに関してはC ++ 03からの大きな前進であることは明らかです。


私が期待= defaultするところに問題があり、そうa=0;ではありませんでした!私は賛成してそれを落とさなければなりませんでした: a(0)。私はまだどれほど便利で= defaultあるかについて混乱しています、それはパフォーマンスについてですか?使用しないとどこか壊れます= defaultか?私はここですべての回答を読んでみましたが、私はいくつかのc ++に慣れていないので、それを理解するのに多くの問題を抱えています。
アクエリアスパワー

@AquariusPower:パフォーマンスに関する「単なる」ものではなく、例外やその他のセマンティクスの周りに必要な場合もあります。つまり、デフォルトの演算子は取るに足らないものである可能性がありますが、デフォルトでない演算子は決して些細なものではあり得ません。コードによっては、メタプログラミング技術を使用して、動作を変更したり、重要な操作のある型を禁止したりします。あなたのa=0例は別の(関連している)トピックである自明な型の振る舞いのためです。
Sean Middleditch、

それはそれが持つことが可能であり= default、それでも付与aされることを意味します=0か 何らかの方法で?「コンストラクター= defaultを用意してフィールドを適切に初期化する方法」のような新しい質問を作成できると思いますか、ところでstruct、a classではなくa で問題があり、アプリをを使用しなくても正しく実行されてい= defaultます。それが良いものであれば、その質問に最小限の構造体を追加します:)
Aquarius Power

1
@AquariusPower:非静的データメンバー初期化子を使用できます。構造体を次のようにstruct { int a = 0; };記述します。コンストラクタが必要だと判断した場合は、それをデフォルトにできますが、型は簡単ではないことに注意してください(これは問題ありません)。
Sean Middleditch

2

std::is_podとその代替案の廃止によりstd::is_trivial && std::is_standard_layout、@ JosephMansfieldの回答からの抜粋は次のようになります。

#include <type_traits>

struct X {
    X() = default;
};

struct Y {
    Y() {}
};

int main() {
    static_assert(std::is_trivial_v<X>, "X should be trivial");
    static_assert(std::is_standard_layout_v<X>, "X should be standard layout");

    static_assert(!std::is_trivial_v<Y>, "Y should not be trivial");
    static_assert(std::is_standard_layout_v<Y>, "Y should be standard layout");
}

ことに注意してくださいY、まだ標準レイアウトのです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.