¿Por qué se pueden modificar los miembros de const en un constructor?


Tengo curiosidad por saber por qué los miembros de const pueden ser modificados en el constructor.

¿Hay alguna regla estándar en la inicialización que anule la "constancia" de un miembro?

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

¿Alguna idea?

Author: Boann, 2018-03-28

3 answers

Esto no es modificación (o asignación) sino inicialización. por ejemplo,

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
    }
};

El miembro de datos const no se puede modificar o asignar, pero podría (y debe) inicializarse a través de la lista de inicializadores de miembros o el inicializador de miembros predeterminado.

Si tanto el inicializador de miembros predeterminado como el inicializador de miembros se proporcionan en el mismo miembro de datos, se ignorará el inicializador de miembros predeterminado. Es por eso que b->b se inicializa con el valor 2.

Si un miembro tiene un inicializador de miembros predeterminado y también aparece en la lista de inicialización de miembros en un constructor, el inicializador de miembros predeterminado se ignora.

Por otro lado, el inicializador de miembros predeterminado solo tiene efecto cuando el miembro de datos no se especifica en la lista de inicializador de miembros. por ejemplo,

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
Author: songyuanyao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-28 14:25:11

Añadiendo a la gran respuesta de songyuanyao, si quieres un const miembro de datos que no puedes inicializar en un constructor, puedes hacer que el miembro 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
    }
};

En C++17 puede mejorar esto aún más al hacerlo 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
    }
};

De esta manera no tendrá problemas con ODR.

 16
Author: Cássio Renan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-28 13:54:31

Cuando lo hagas:

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

Le estás diciendo al compilador que haga esto con el constructor predeterminado:

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

Independientemente de si ha proporcionado el constructor predeterminado o no. Cuando proporciona default-constructor y la asignación inicial, sobrescribe el código de asignación predeterminado del compilador (es decir, b(5)). La inicialización/asignación predeterminada en la declaración es útil cuando tiene varios constructores, y puede o no asignar los miembros const en todos constructores:

...
Bar() = default; // b=5
Bar(int x) : b(x) // b=x
Bar(double y) : /*other init, but not b*/  // b=5
...
 2
Author: Ajay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-04 03:15:43