web-dev-qa-db-ja.com

コンストラクターでconstメンバーを変更できるのはなぜですか?

Constメンバーをコンストラクターで変更できるのはなぜですか。

メンバーの「定数」をオーバーライドする初期化の標準ルールはありますか?

struct Bar {
    const int b = 5; // default member initialization
    Bar(int c):b(c) {}
};

Bar *b = new Bar(2); // Problem: Bar::b is modified to 2
                     // was expecting it to be an error

何か案は?

43
Joseph D.

これは変更(または割り当て)ではなく、 初期化 です。例えば.

struct Bar {
    const int b = 5; // initialization (via default member initializer)
    Bar(int c)
        :b(c)        // initialization (via member initializer list)
    {
        b = c;       // assignment; which is not allowed
    }
};

constデータメンバーは変更または割り当てできませんが、メンバー初期化子リストまたはデフォルトのメンバー初期化子を介して初期化することができます(必要です)。

デフォルトのメンバー初期化子とメンバー初期化子の両方が同じデータメンバーで提供されている場合、デフォルトのメンバー初期化子は無視されます。 b->bが値2で初期化されるのはそのためです。

メンバーにデフォルトのメンバー初期化子があり、コンストラクターのメンバー初期化リストにも表示されている場合、デフォルトのメンバー初期化子は無視されます。

一方、デフォルトのメンバー初期化子は、データメンバーがメンバー初期化子リストで指定されていない場合にのみ有効になります。例えば.

struct Bar {
    const int b = 5;   // default member initialization
    Bar(int c):b(c) {} // b is initialized with c
    Bar() {}           // b is initialized with 5
};
66
songyuanyao

Songyuanyaoの素晴らしい答えに加えて、コンストラクターで初期化できないconstデータメンバーが必要な場合は、メンバーstaticを作成できます。

struct Bar {
    static const int b = 5; // static member initialization
    Bar(int c)
        :b(c)        // Error: static data member can only be initialized at its definition
    {
        b = c;       // Error: b is read-only
    }
};

C++ 17では、inlineにすることでこれをさらに改善できます。

struct Bar {
    inline static const int b = 5; // static member initialization
    Bar(int c)
        :b(c)        // Error: static data member can only be initialized at its definition
    {
        b = c;       // Error: b is read-only
    }
};

これにより、ODRで問題が発生しなくなります。

16
Cássio Renan

あなたがするとき:

_struct Bar {
    const int b = 5; // default member initialization
    ...
};
_

デフォルトのコンストラクターでこれを行うようコンパイラーに指示しています。

_...
Bar() : b(5) 
{}
...
_

デフォルトのコンストラクタを提供したかどうかに関係なく。 default-constructor andの初期割り当てを指定すると、コンパイラのデフォルト割り当てコード(つまり、b(5))がオーバーライドされます。宣言でのデフォルトの初期化/割り当ては、複数のコンストラクターがあり、すべてのコンストラクターでconstメンバーを割り当てることができる場合とできない場合に役立ちます。

_...
Bar() = default; // b=5
Bar(int x) : b(x) // b=x
Bar(double y) : /*other init, but not b*/  // b=5
...
_
2
Ajay