A maior parte da análise: por que não A (()); trabalhos?

Entre as muitas coisas que o Stack Overflow me ensinou está o que é conhecido como “o mais complicado”, que é classicamente demonstrado com uma linha como

A a(B()); //declares a function 

Enquanto isso, para a maioria, intuitivamente parece ser a declaração de um object a do tipo A , tomando um object temporário B como um parâmetro construtor, é na verdade uma declaração de uma function retornando um A , tomando um ponteiro para uma function que retorna B e em si não tem parâmetros. Da mesma forma a linha

 A a(); //declares a function 

também se enquadra na mesma categoria, pois em vez de um object, ele declara uma function. Agora, no primeiro caso, a solução usual para esse problema é adicionar um conjunto extra de colchetes / parênteses ao redor do B() , pois o compilador o interpretará como a declaração de um object.

 A a((B())); //declares an object 

No entanto, no segundo caso, fazendo o mesmo leva a um erro de compilation

 A a(()); //compile error 

Minha pergunta é: por quê? Sim, estou muito ciente de que a solução correta é alterá-la para A a; , mas estou curioso para saber o que é que o extra () faz para o compilador no primeiro exemplo que então não funciona quando reaplicando-o no segundo exemplo. É o A a((B())); contornar uma exceção específica escrita no padrão?

Não há resposta esclarecida, é só porque não é definida como uma syntax válida pela linguagem C ++ … Então é assim, por definição da linguagem.

Se você tiver uma expressão dentro, ela será válida. Por exemplo:

  ((0));//compiles 

Posicionamento ainda mais simples: porque (x) é uma expressão C ++ válida, enquanto que () não é.

Para aprender mais sobre como as linguagens são definidas e como os compiladores funcionam, você deve aprender sobre a Teoria da Linguagem Formal ou, mais especificamente, Gramática Livre de Contexto (CFG) e material relacionado, como máquinas de estados finitos. Se você estiver interessado nisso, embora as páginas da Wikipédia não sejam suficientes, você terá que comprar um livro.

A solução final para esse problema é mover para a syntax de boot uniforme do C + 11, se possível.

 A a{}; 

http://www.stroustrup.com/C++11FAQ.html#uniform-init

Declaradores de function C

Primeiro de tudo, há C. Em C, A a() é declaração de function. Por exemplo, putchar tem a seguinte declaração. Normalmente, essas declarações são armazenadas em arquivos de header, no entanto, nada impede que você as grave manualmente, se você souber como é a declaração de function. Os nomes dos argumentos são opcionais nas declarações, então omiti isso neste exemplo.

 int putchar(int); 

Isso permite que você escreva o código assim.

 int puts(const char *); int main() { puts("Hello, world!"); } 

C também permite que você defina funções que tomam funções como argumentos, com uma syntax agradável legível que se parece com uma chamada de function (bem, é legível, desde que você não retorne um ponteiro para function).

 #include  int eighty_four() { return 84; } int output_result(int callback()) { printf("Returned: %d\n", callback()); return 0; } int main() { return output_result(eighty_four); } 

Como mencionei, C permite omitir nomes de argumentos em arquivos de header, portanto, o output_result ficaria assim no arquivo de header.

 int output_result(int()); 

Um argumento no construtor

Você não reconhece esse? Bem, deixe-me lembrá-lo.

 A a(B()); 

Sim, é exatamente a mesma declaração de function. A é int , a é output_result e B é int .

Você pode facilmente notar um conflito de C com novos resources do C ++. Para ser exato, construtores sendo nome de class e parênteses, e syntax de declaração alternativa com () invés de = . Por padrão, o C ++ tenta ser compatível com o código C e, portanto, tem que lidar com este caso – mesmo que praticamente ninguém se importe. Portanto, os resources antigos do C têm prioridade sobre os novos resources do C ++. A gramática de declarações tenta corresponder o nome como function, antes de reverter para a nova syntax com () se falhar.

Se um desses resources não existir, ou tiver uma syntax diferente (como {} em C ++ 11), esse problema nunca teria ocorrido para a syntax com um argumento.

Agora você pode perguntar por que A a((B())) funciona. Bem, vamos declarar output_result com parênteses inúteis.

 int output_result((int())); 

Não vai funcionar. A gramática requer que a variável não esteja entre parênteses.

 :1:19: error: expected declaration specifiers or '...' before '(' token 

No entanto, o C ++ espera a expressão padrão aqui. Em C ++, você pode escrever o seguinte código.

 int value = int(); 

E o seguinte código.

 int value = ((((int())))); 

C ++ espera que a expressão dentro de parênteses seja … bem … expressão, ao contrário do tipo C espera. Parênteses não significam nada aqui. No entanto, ao inserir parênteses inúteis, a declaração da function C não é correspondida e a nova syntax pode ser correspondida corretamente (o que simplesmente espera uma expressão, como 2 + 2 ).

Mais argumentos no construtor

Certamente um argumento é bom, mas e quanto a dois? Não é que os construtores possam ter apenas um argumento. Uma das classs internas que recebe dois argumentos é std::string

 std::string hundred_dots(100, '.'); 

Está tudo bem e bem (tecnicamente, seria mais complicado se fosse escrito como std::string wat(int(), char()) , mas sejamos honestos – quem escreveria isso? código tem um problema vexatório.Você assumiria que você tem que colocar tudo entre parênteses.

 std::string hundred_dots((100, '.')); 

Não é bem assim.

 :2:36: error: invalid conversion from 'char' to 'const char*' [-fpermissive] In file included from /usr/include/c++/4.8/string:53:0, from :1: /usr/include/c++/4.8/bits/basic_string.tcc:212:5: error: initializing argument 1 of 'std::basic_string<_chart , _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits; _Alloc = std::allocator]' [-fpermissive] basic_string<_chart , _Traits, _Alloc>:: ^ 

Eu não tenho certeza porque g + + tenta converter char para const char * . De qualquer forma, o construtor foi chamado com apenas um valor do tipo char . Não há sobrecarga que tenha um argumento do tipo char , portanto, o compilador é confuso. Você pode perguntar – por que o argumento é do tipo char?

 (100, '.') 

Sim, aqui está um operador de vírgula. O operador de vírgula recebe dois argumentos e fornece o argumento do lado direito. Não é realmente útil, mas é algo a ser conhecido pela minha explicação.

Em vez disso, para resolver a análise mais problemática, o código a seguir é necessário.

 std::string hundred_dots((100), ('.')); 

Os argumentos estão entre parênteses, não a expressão inteira. Na verdade, apenas uma das expressões precisa estar entre parênteses, já que é suficiente quebrar a gramática C para usar o recurso C ++. As coisas nos trazem ao ponto de zero argumentos.

Zero argumentos no construtor

Você deve ter notado a function eighty_four na minha explicação.

 int eighty_four(); 

Sim, isso também é afetado pela análise mais problemática. É uma definição válida e uma que você provavelmente já viu se criou arquivos de header (e você deveria). Adicionar parênteses não corrige isso.

 int eighty_four(()); 

Por que? Bem, () não é uma expressão. Em C ++, você tem que colocar uma expressão entre parênteses. Você não pode escrever auto value = () em C ++, porque () não significa nada (e mesmo se tivesse, como uma tupla vazia (veja Python), seria um argumento, não zero). Praticamente isso significa que você não pode usar a syntax abreviada sem usar a syntax {} C ++ 11, pois não há expressões para colocar entre parênteses, e a gramática C para declarações de function sempre se aplicará.

Você poderia ao invés

 A a(()); 

usar

 A a=A(); 

O parens mais interno em seu exemplo seria uma expressão, e em C ++ a gramática define uma expression para ser uma assignment-expression ou outra expression seguida por uma vírgula e outra assignment-expression (Apêndice A.4 – Resumo da gramática / expressões).

A gramática define ainda uma assignment-expression como um dos vários outros tipos de expressão, nenhum dos quais pode ser nada (ou apenas espaço em branco).

Portanto, a razão pela qual você não pode ter A a(()) é simplesmente porque a gramática não permite isso. No entanto, não posso responder por que as pessoas que criaram o C ++ não permitiram esse uso específico de parentes vazios como algum tipo de caso especial – eu acho que eles prefeririam não colocar um caso tão especial se houvesse uma alternativa razoável.