C declaradores de funciones
En primer lugar, hay C. En C, A a()es la declaración de función. Por ejemplo, putchartiene la siguiente declaración. Normalmente, tales declaraciones se almacenan en archivos de encabezado, sin embargo, nada le impide escribirlas manualmente, si sabe cómo se ve la declaración de función. Los nombres de los argumentos son opcionales en las declaraciones, por lo que lo omití en este ejemplo.
int putchar(int);
Esto le permite escribir el código de esta manera.
int puts(const char *);
int main() {
puts("Hello, world!");
}
C también le permite definir funciones que toman funciones como argumentos, con una buena sintaxis legible que parece una llamada a función (bueno, es legible, siempre que no devuelva un puntero a la función).
#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 mencioné, C permite omitir nombres de argumentos en los archivos de encabezado, por lo tanto, output_resultse vería así en el archivo de encabezado.
int output_result(int());
Un argumento en constructor
¿No lo reconoces? Bueno, déjame recordarte.
A a(B());
Sí, es exactamente la misma declaración de función. Aes int, aes output_resulty Bes int.
Puede notar fácilmente un conflicto de C con las nuevas características de C ++. Para ser exactos, los constructores son nombre de clase y paréntesis, y sintaxis de declaración alternativa con en ()lugar de =. Por diseño, C ++ intenta ser compatible con el código C y, por lo tanto, tiene que lidiar con este caso, incluso si prácticamente a nadie le importa. Por lo tanto, las características antiguas de C tienen prioridad sobre las nuevas características de C ++. La gramática de las declaraciones intenta hacer coincidir el nombre como función, antes de volver a la nueva sintaxis con ()si falla.
Si una de esas características no existiera, o tuviera una sintaxis diferente (como {}en C ++ 11), este problema nunca hubiera ocurrido para la sintaxis con un argumento.
Ahora puede preguntar por qué A a((B()))funciona. Bueno, declaremos output_resultcon paréntesis inútiles.
int output_result((int()));
No va a funcionar La gramática requiere que la variable no esté entre paréntesis.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Sin embargo, C ++ espera una expresión estándar aquí. En C ++, puede escribir el siguiente código.
int value = int();
Y el siguiente código.
int value = ((((int()))));
C ++ espera que la expresión dentro de paréntesis sea ... bueno ... expresión, a diferencia del tipo C que espera. Los paréntesis no significan nada aquí. Sin embargo, al insertar paréntesis inútiles, la declaración de la función C no coincide, y la nueva sintaxis puede coincidir correctamente (lo que simplemente espera una expresión, como 2 + 2).
Más argumentos en constructor
Seguramente un argumento es bueno, pero ¿y dos? No es que los constructores puedan tener un solo argumento. Una de las clases integradas que toma dos argumentos esstd::string
std::string hundred_dots(100, '.');
Todo esto está muy bien (técnicamente, sería más irritante si se escribiera como std::string wat(int(), char()), pero seamos honestos, ¿quién escribiría eso? Pero supongamos que este código tiene un problema molesto. Asumirías que tienes que poner todo entre paréntesis.
std::string hundred_dots((100, '.'));
No del todo.
<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>::
^
No estoy seguro de por qué g ++ intentos para convertir chara const char *. De cualquier manera, se llamó al constructor con solo un valor de tipo char. No hay sobrecarga que tenga un argumento de tipo char, por lo tanto, el compilador está confundido. Usted puede preguntar: ¿por qué el argumento es de tipo char?
(100, '.')
Sí, ,aquí hay un operador de coma. El operador de coma toma dos argumentos y da el argumento del lado derecho. No es realmente útil, pero es algo que se debe conocer por mi explicación.
En cambio, para resolver el análisis más irritante, se necesita el siguiente código.
std::string hundred_dots((100), ('.'));
Los argumentos están entre paréntesis, no toda la expresión. De hecho, solo una de las expresiones debe estar entre paréntesis, ya que es suficiente para romper ligeramente la gramática de C para usar la función C ++. Las cosas nos llevan al punto de cero argumentos.
Cero argumentos en constructor
Es posible que haya notado la eighty_fourfunción en mi explicación.
int eighty_four();
Sí, esto también se ve afectado por el análisis más irritante. Es una definición válida, y una que probablemente haya visto si creó archivos de encabezado (y debería). Agregar paréntesis no lo arregla.
int eighty_four(());
¿Por qué es así? Bueno, ()no es una expresión. En C ++, debe poner una expresión entre paréntesis. No puede escribir auto value = ()en C ++, porque ()no significa nada (e incluso si lo hiciera, como una tupla vacía (ver Python), sería un argumento, no cero). Prácticamente eso significa que no puede usar la sintaxis abreviada sin usar la sintaxis de C ++ 11 {}, ya que no hay expresiones para poner entre paréntesis, y la gramática de C para las declaraciones de funciones siempre se aplicará.
(B())es solo una expresión de C ++, nada más. No es ningún tipo de excepción. La única diferencia que hace es que no hay forma de que pueda analizarse como un tipo, por lo que no lo es.