c++ - g++ rechazos, clang++ acepta: foo(x)("bar")("baz");
syntax most-vexing-parse (2)
Por lo que puedo decir, esto se trata en el borrador de la norma de C ++, sección 6.8
Resolución de ambigüedad, que dice que puede haber una ambigüedad entre las declaraciones de expresión y las declaraciones y dice:
Hay una ambigüedad en la gramática que involucra expresiones de expresión y declaraciones: una declaración de expresión con una conversión de tipo explícito de estilo de función (5.2.3) como su subexpresión más a la izquierda puede ser indistinguible de una declaración donde el primer declarador comienza con una (. En esos casos, la declaración es una declaración. [Nota: Para desambiguar, la declaración completa podría tener que ser examinada para determinar si es una expresión-declaración o una declaración. Esto desmarca muchos ejemplos. [Ejemplo: suponiendo que T es un tipo simple -Especificador (7.1.6),
y da los siguientes ejemplos:
T(a)->m = 7; // expression-statement
T(a)++; // expression-statement
T(a,5)<<c; // expression-statement
T(*d)(int); // declaration
T(e)[5]; // declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration
y luego dice:
Los casos restantes son declaraciones. [Ejemplo:
class T { // ... public: T(); T(int); T(int, int); }; T(a); // declaration T(*b)(); // declaration T(c)=7; // declaration T(d),e,f=3; // declaration extern int h; T(g)(h,2); // declaration
—En el ejemplo] —en la nota final]
Parece que este caso cae en los ejemplos de declaración, en particular, el último ejemplo parece justificar el caso en el OP, por lo que gcc
sería correcto entonces.
La sección relevante mencionada anteriormente 5.2.3
Conversión explícita de tipo (notación funcional) dice:
[...] Si el tipo especificado es un tipo de clase, el tipo de clase deberá estar completo. Si la lista de expresiones especifica más de un solo valor, el tipo será una clase con un constructor debidamente declarado (8.5, 12.1), y la expresión T (x1, x2, ...) es equivalente en efecto a la declaración T t (x1, x2, ...); para algunas variables temporales inventadas t, con el resultado es el valor de t como prvalue.
y 8.3
Significado de los declaradores que dice:
En una declaración TD donde D tiene la forma
( D1 )
el tipo del identificador-declarante contenido es el mismo que el del identificador-declarante contenido en la declaración
T D1
Los paréntesis no alteran el tipo del identificador-declarador incorporado, pero pueden alterar el enlace de los declaradores complejos.
Actualizar
Originalmente estaba usando N337 pero si miramos N4296 sección 6.8
se actualizó y ahora incluye la siguiente nota:
Si la declaración no puede ser sintácticamente una declaración, no hay ambigüedad, por lo que esta regla no se aplica.
lo que significa que gcc
es incorrecto ya que:
foo x ("bar")("baz");
no puede ser una declaración válida, originalmente interpreté el párrafo 2
como diciendo que si su caso comienza con alguno de los siguientes, entonces es una declaración, que es quizás la forma en que el implementador de gcc
interpretó también.
Debería haber sospechado más del párrafo 2
ya que la única parte normativa del párrafo 2
realmente no dice nada con respecto al párrafo 1
y parece imponer un requisito en un ejemplo que no es normativo. Podemos ver que la declaración del párrafo 2
es ahora una nota que tiene mucho más sentido.
Como TC señaló a continuación, el párrafo 2
realidad nunca fue normativo, simplemente apareció de esa manera y se vinculó al cambio que lo solucionó .
Alguien había asked el otro día por qué algo se compila con un sonido metálico, pero no con gcc. Comprendí intuitivamente lo que estaba sucediendo y pude ayudar a la persona, pero me pregunté: según el estándar, ¿qué compilador era el correcto? Aquí hay una versión resumida del código:
#include <iostream>
#include <string>
class foo
{
public:
foo(const std::string& x):
name(x)
{ }
foo& operator()(const std::string& x)
{
std::cout << name << ": " << x << std::endl;
return (*this);
}
std::string name;
};
int main()
{
std::string x = "foo";
foo(x)("bar")("baz");
return 0;
}
Esto compila bien con clang ++, pero g ++ da el siguiente error:
runme.cpp: In function ‘int main()’:
runme.cpp:21:11: error: conflicting declaration ‘foo x’
foo(x)("bar")("baz");
^
runme.cpp:20:17: error: ‘x’ has a previous declaration as ‘std::string x’
std::string x = "foo";
Si agrego un par de paréntesis en la línea 21, g ++ está contento:
(foo(x))("bar")("baz");
En otras palabras, g ++ interpreta esta línea como:
foo x ("bar")("baz");
Me parece que es un error en g ++, pero una vez más, quería preguntar a los expertos estándar, ¿qué compilador se equivocó?
PS: gcc-4.8.3, clang-3.5.1
Si quitamos la linea
std::string x = "foo";
entonces g ++ se queja de:
foo(x)("bar")("baz");
con el error de sintaxis:
foo.cc:20:18: error: expected '','' or '';'' before ''('' token
foo(x)("bar")("baz");
No veo cómo foo (x)("bar")("baz");
podría ser una declaración válida, y aparentemente g ++ tampoco puede. La línea foo x("bar")("baz");
Se rechaza con el mismo error.
La "resolución de ambigüedad" mencionada en la publicación de Shafik solo se activa cuando la expresión-declaración es sintácticamente indistinguible de una declaración. Sin embargo, en este caso no es una sintaxis de declaración válida, por lo que no hay ambigüedad, debe ser una expresión-declaración.
g ++ no puede procesar la línea como una expresión-expresión por lo que es un error de g ++.
Esto es inquietantemente similar a este error g ++ recientemente discutido en SO; Parece que g ++ quizás está decidiendo demasiado pronto en el procesamiento que la línea debe ser una declaración.