¿Realmente necesito implementar el constructor proporcionado por el usuario para los objetos const?


Tengo el código:

class A {
  public:
    A() = default;

  private:
    int i = 1;
};

int main() {
  const A a;
  return 0;
}

Compila bien en g++ (ver ideone), pero falla en clang++ con error:

La inicialización predeterminada de un objeto de tipo const 'const A' requiere un constructor predeterminado proporcionado por el usuario

Reporté este problema en LLVM bug-tracker y lo OBTUVE INVÁLIDO.

Veo absolutamente inútil tratar de convencer a los desarrolladores de clang. Por otro lado, no veo la razón para tal restricción.


¿Alguien puede aconsejar, si el estándar C++11 de alguna manera implica que este código no es válido? ¿O simplemente debo reportar un error a g++? ¿O tal vez hay suficiente libertad en las reglas del lenguaje para manejar este código de muchas maneras?

Author: abyss.7, 2014-02-20

3 answers

N3797 §8.5 / 7 dice:

Si un programa pide la inicialización predeterminada de un objeto de tipo T calificado por const, T será un tipo de clase con un constructor predeterminado proporcionado por el usuario.

No hay más ejemplo o explicación de esto. Estoy de acuerdo en que parece bastante extraño. Además, la regla se actualizó en C++11 para ser más restrictiva que en C++03, cuando los tipos de clase necesitaban constructores declarados por el usuario. (Su constructor es declarado por el usuario.)

La solución es pedir la inicialización del valor usando {}, o usar la definición inteligente fuera de clase de Dietmar inline.

GCC proporciona un diagnóstico (y bastante bueno, refiriéndose a los nuevos requisitos de C++11) si agrega otro miembro sin un inicializador.

  private:
    int i = 1;
    int j;

 

unmem.cpp:11:11: error: uninitialized const ‘a’ [-fpermissive]
   const A a;
           ^
unmem.cpp:1:7: note: ‘const class A’ has no user-provided default constructor
 class A {
       ^
unmem.cpp:3:5: note: constructor is not user-provided because it is explicitly defaulted in the class body
     A() = default;
     ^
unmem.cpp:7:9: note: and the implicitly-defined constructor does not initialize ‘int A::j’
     int j;

El CCG fuente se refiere a DR 253, ¿Por qué se deben inicializar objetos const vacíos o completamente inicializados? Este es un tema abierto en el estándar, actualizado por última vez en agosto de 2011 (post-C++11) con esta nota:

Si el constructor implícito por defecto inicializa todos los subobjetos, no se requiere inicializador.

Por lo tanto, mientras que Clang cumple con C++11 (y cumplirá tal cual con C++14), GCC está implementando las últimas ideas del comité de estandarización.

Presentó un error de CCG . Predigo que necesitarás -pedantic para obtener un diagnóstico cuando (y si) se corrija el error.

 23
Author: Potatoswatter,
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
2014-02-20 08:54:40

Tenga en cuenta que puede convertir su clase fácilmente en una que tenga un constructor predeterminado definido por el usuario:

class A {
  public:
    A();

  private:
    int i = 1;
};

inline A::A() = default;

Según 8.4.2 [dcl.fct.def.por defecto] párrafo 4:

... Una función miembro especial es proporcionada por el usuario si está declarada por el usuario y no explícitamente impago o borrado en su primera declaración. ...

Esto indica implícitamente que una función que no está explícitamente predeterminada en su primera declaración no es proporcionada por el usuario. En combinación con 8,5 [dcl.init] párrafo 6

... Si un programa pide la inicialización predeterminada de un objeto de tipo T calificado por const, T será un tipo de clase con un constructor predeterminado proporcionado por el usuario.

Parece claro que no puede usar un constructor predeterminado en su primera declaración para inicializar un objeto const. Sin embargo, puede usar una definición predeterminada si no es la primera declaración como se hace en el código anterior.

 22
Author: Dietmar Kühl,
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
2014-02-20 07:30:35

Editar: Lo siguiente se basa en información desactualizada. Acabo de pasar por N3797 y esto es lo que encontré:

§ 8.5/7 [dcl.init]
Si un programa pide la inicialización predeterminada de un objeto de tipo T calificado para const, T será un tipo de clase con un constructor predeterminado proporcionado por el usuario .

Tenga en cuenta que la cita estándar en el siguiente enlace dice declarado por el usuario.


El siguiente programa compila en g++ pero no clang++:

struct A {};

void f()
{
  A const a;
}

Y podría estar relacionado con este informe de error donde fue "arreglado". g++ no puede compilarlo una vez que contiene miembros de datos a menos que se inicialicen. Tenga en cuenta que int member = 1 ya no hará A un POD. Comparativamente, clang++ rechaza todas las permutaciones (clases vacías y miembros de datos inicializados o no.) Para una interpretación de lo que la norma significa por el siguiente párrafo:

§ 8.5/9 [dcl.init] dice:

Si no hay inicializador especificado para un objeto, y el objeto es de tipo de clase no POD (o matriz de la misma), el objeto se inicializará por defecto; si el objeto es de tipo const calificado, el tipo de clase subyacente tendrá un constructor predeterminado declarado por el usuario. De lo contrario, si no hay inicializador especificado para un objeto, el objeto y sus subobjetos, si los hay, tienen un valor inicial indeterminado; si el objeto o cualquiera de sus subobjetos son de tipo const-calificado, el programa es mal formado.

Ver ¿Por qué C++ requiere un constructor predeterminado proporcionado por el usuario para construir por defecto un objeto const?. Supuestamente el programa está mal formado if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized). Tenga en cuenta cómo se comporta g++ para lo siguiente:

struct A {int a;};
struct B {int a = 1;};
int main() 
{
    A a;
    B b;
    const A c; // A is POD, error
    const B d; // B is not POD, contains data member initializer, no error
}
 4
Author: ,
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
2014-02-20 07:46:02