O código a seguir é bastante trivial e eu esperava que ele compilasse bem.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
Testei este código com g ++ versão 4.7.2, 4.8.1, clang ++ 3.2 e 3.3. Além do fato de que g ++ 4.7.2 segfaults neste código ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ), os outros compiladores testados fornecem mensagens de erro que não explicam muito.
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 e 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())
^
Tornar este código compilável é possível e parece que não deve fazer diferença. Existem duas opções:
struct B
{
int i = 0;
B(){} // using B()=default; works only for clang++
};
ou
struct B
{
int i;
B() : i(0) {} // classic c++98 initialization
};
Este código está realmente incorreto ou os compiladores estão errados?
c++
c++11
language-lawyer
etam1024
fonte
fonte
internal compiler error: Segmentation fault
a este código ...int i = 0
menos que sejastatic const int i = 0
.B()
como uma chamada de função para um construtor. Você nunca "chama" diretamente um construtor. Pense nisso como uma sintaxe especial que cria um temporárioB
... e o construtor é invocado como apenas uma parte desse processo, nas profundezas do mecanismo que se segue.B
parece fazer isso funcionar emgcc 4.7
.Respostas:
Bem, nenhum. O padrão tem um defeito - ele diz que
A
é considerado completo ao analisar o inicializador paraB::i
e aqueleB::B()
(que usa o inicializador paraB::i
) pode ser usado dentro da definição deA
. Isso é claramente cíclico. Considere isto:Isso tem uma contradição:
B::B()
é implicitamentenoexcept
iffA()
não joga eA()
não joga iff nãoB::B()
é . Existem vários outros ciclos e contradições nesta área.noexcept
Isso é rastreado pelos problemas principais 1360 e 1397 . Observe, em particular, esta nota na edição principal 1397:
Esse é um caso especial da regra que implementei no Clang para resolver esse problema. A regra de Clang é que um construtor padrão padronizado para uma classe não pode ser usado antes que os inicializadores de membros de dados não estáticos para essa classe sejam analisados. Portanto, o Clang emite um diagnóstico aqui:
... porque o Clang analisa os argumentos padrão antes de analisar os inicializadores padrão, e este argumento padrão exigiria que
B
os inicializadores padrão de já tivessem sido analisados (a fim de definir implicitamenteB::B()
).fonte
Talvez este seja o problema:
Portanto, o construtor padrão é gerado quando consultado pela primeira vez, mas a pesquisa falhará porque A não está completamente definido e B dentro de A, portanto, não será encontrado.
fonte
B b
não é um problema, e encontrar métodos explícitos / um construtor explicitamente declarado emB
não é um problema. Portanto, seria bom ver alguma definição de por que a pesquisa deve proceder de forma diferente aqui, de modo que "B
dentroA
não seja encontrado" apenas neste caso, mas não nos outros, antes que possamos declarar o código ilegal por esse motivo.=0
dei = 0;
. Mas sem isso=0
, o código é válido e você não encontrará um único compilador que reclame do usoB()
dentro da definição deA
.