Declaradores de função C
Primeiro de tudo, há C. Em C, A a()
há declaração de função. Por exemplo, putchar
tem a seguinte declaração. Normalmente, essas declarações são armazenadas nos arquivos de cabeçalho; no entanto, nada impede que você as escreva manualmente, se você souber como é a declaração da função. Os nomes dos argumentos são opcionais nas declarações, então eu o omiti 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 definir funções que aceitam funções como argumentos, com uma sintaxe legível que se parece com uma chamada de função (bem, é legível, desde que você não retorne um ponteiro para a função).
#include <stdio.h>
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 eu mencionei, C permite omitir nomes de argumentos nos arquivos de cabeçalho, portanto, output_result
ficaria assim no arquivo de cabeçalho.
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 função. A
é int
, a
é output_result
e B
é int
.
Você pode perceber facilmente um conflito de C com novos recursos do C ++. Para ser exato, construtores sendo o nome da classe e parênteses, e sintaxe de declaração alternativa com em ()
vez de =
. Por design, o C ++ tenta ser compatível com o código C e, portanto, precisa lidar com esse caso - mesmo que praticamente ninguém se importe. Portanto, os recursos antigos do C têm prioridade sobre os novos recursos do C ++. A gramática das declarações tenta corresponder o nome como função, antes de reverter para a nova sintaxe ()
se ela falhar.
Se um desses recursos não existisse ou tivesse uma sintaxe diferente (como {}
no C ++ 11), esse problema nunca teria acontecido para a sintaxe 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()));
Isso não vai funcionar. A gramática requer que a variável não esteja entre parênteses.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
No entanto, C ++ espera 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()))));
O C ++ espera que a expressão dentro dos parênteses seja ... bem ... expressão, em oposição ao tipo C espera. Parênteses não significam nada aqui. No entanto, inserindo parênteses inúteis, a declaração da função C não é correspondida e a nova sintaxe pode ser correspondida corretamente (o que simplesmente espera uma expressão, como 2 + 2
).
Mais argumentos no construtor
Certamente um argumento é bom, mas e dois? Não é que os construtores possam ter apenas um argumento. Uma das classes internas que recebe dois argumentos éstd::string
std::string hundred_dots(100, '.');
Está tudo bem (tecnicamente, teria a análise mais irritante se fosse escrita como std::string wat(int(), char())
, mas sejamos honestos - quem escreveria isso? Mas vamos assumir que esse código tenha um problema irritante. Você supõe que deve colocar tudo entre parênteses.
std::string hundred_dots((100, '.'));
Não é bem assim.
<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
from <stdin>: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<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string<_CharT, _Traits, _Alloc>::
^
Não sei por que o g ++ tenta converter char
para const char *
. De qualquer maneira, 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 está 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 irritante, é necessário o seguinte código.
std::string hundred_dots((100), ('.'));
Os argumentos estão entre parênteses, não a expressão inteira. De fato, apenas uma das expressões precisa estar entre parênteses, pois é suficiente interromper um pouco a gramática C para usar o recurso C ++. As coisas nos levam ao ponto de zero argumentos.
Zero argumento no construtor
Você pode ter notado a eighty_four
função na minha explicação.
int eighty_four();
Sim, isso também é afetado pela análise mais irritante. É uma definição válida e provavelmente você já viu se criou arquivos de cabeçalho (e deveria). Adicionar parênteses não o corrige.
int eighty_four(());
Por que? Bem, ()
não é uma expressão. No C ++, você deve colocar uma expressão entre parênteses. Você não pode escrever auto value = ()
em C ++, porque ()
isso não significa nada (e mesmo que isso significasse, como tupla vazia (consulte Python), seria um argumento, não zero). Na prática, isso significa que você não pode usar a sintaxe abreviada sem usar a {}
sintaxe do C ++ 11 , pois não há expressões entre parênteses e a gramática C para declarações de função sempre será aplicada.
(B())
é apenas uma expressão C ++, nada mais. Não é nenhum tipo de exceção. A única diferença que faz é que não há como ele ser analisado como um tipo e, portanto, não é.