El siguiente código es bastante trivial y esperaba que se compilara bien.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
He probado este código con g ++ versión 4.7.2, 4.8.1, clang ++ 3.2 y 3.3. Aparte del hecho de que g ++ 4.7.2 segfaults en este código ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ), los otros compiladores probados dan mensajes de error que no explican mucho.
g ++ 4.8.1:
test.cpp: In constructor ‘constexpr A::B::B()’:
test.cpp:3:12: error: constructor required before non-static data member for ‘A::B::i’ has been parsed
struct B
^
test.cpp: At global scope:
test.cpp:11:23: note: synthesized method ‘constexpr A::B::B()’ first required here
A(const B& _b = B())
^
clang ++ 3.2 y 3.3:
test.cpp:11:21: error: defaulted default constructor of 'B' cannot be used by non-static data member initializer which appears before end of class definition
A(const B& _b = B())
^
Hacer que este código sea compilable es posible y parece que no debería hacer ninguna diferencia. Hay dos opciones:
struct B
{
int i = 0;
B(){} // using B()=default; works only for clang++
};
o
struct B
{
int i;
B() : i(0) {} // classic c++98 initialization
};
¿Es este código realmente incorrecto o los compiladores están equivocados?
c++
c++11
language-lawyer
etam1024
fuente
fuente
internal compiler error: Segmentation fault
a este código ...int i = 0
menos que lo seastatic const int i = 0
.B()
como una llamada de función a un constructor. Usted no directamente "llamada" un constructor. Piense en esto como una sintaxis especial que crea un temporalB
... y el constructor se invoca como solo una parte de ese proceso, en lo profundo del mecanismo que sigue.B
parece hacer que esto funcionegcc 4.7
.Respuestas:
Bueno, tampoco. El estándar tiene un defecto: dice tanto que
A
se considera completo al analizar el inicializador paraB::i
como queB::B()
(que usa el inicializador paraB::i
) se puede usar dentro de la definición deA
. Eso es claramente cíclico. Considera esto:Esto tiene una contradicción:
B::B()
es implícitamentenoexcept
iffA()
no lanza, yA()
no lanza iff no loB::B()
es . Hay varios otros ciclos y contradicciones en esta área.noexcept
Esto se rastrea mediante los problemas centrales 1360 y 1397 . Tenga en cuenta en particular esta nota en el número principal 1397:
Ese es un caso especial de la regla que implementé en Clang para resolver este problema. La regla de Clang es que un constructor predeterminado predeterminado para una clase no se puede usar antes de que se analicen los inicializadores de miembros de datos no estáticos para esa clase. Por lo tanto, Clang emite un diagnóstico aquí:
... porque Clang analiza los argumentos predeterminados antes de analizar los inicializadores predeterminados, y este argumento predeterminado requeriría que
B
los inicializadores predeterminados ya se hayan analizado (para poder definirlos implícitamenteB::B()
).fuente
Tal vez éste es el problema:
Entonces, el constructor predeterminado se genera cuando se busca por primera vez, pero la búsqueda fallará porque A no está completamente definido y, por lo tanto, B dentro de A no se encontrará.
fuente
B b
no es un problema, y encontrar métodos explícitos / un constructor declarado explícitamenteB
no es un problema. Por lo que sería bueno ver una definición de por qué la búsqueda debe proceder de manera diferente aquí, así que "B
en el interiorA
no se encuentra" en tan sólo este único caso, pero no los otros, antes de que podamos declarar el código ilegal por esta razón.=0
archivoi = 0;
. Pero sin eso=0
, el código es válido y no encontrará un solo compilador que se queje del usoB()
dentro de la definición deA
.