Como seguimiento interesante (aunque no de gran importancia práctica) a mi pregunta anterior: ¿Por qué C ++ nos permite rodear el nombre de la variable entre paréntesis cuando declaramos una variable?
Descubrí que combinar la declaración entre paréntesis con la característica de nombre de clase inyectada puede llevar a resultados sorprendentes con respecto al comportamiento del compilador.
Eche un vistazo al siguiente programa:
#include <iostream>
struct B
{
};
struct C
{
C (){ std::cout << "C" << '\n'; }
C (B *) { std::cout << "C (B *)" << '\n';}
};
B *y = nullptr;
int main()
{
C::C (y);
}
Compilar con g ++ 4.9.2 me da el siguiente error de compilación:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
Se compila con éxito con MSVC2013 / 2015 e imprime
C (B *)
Se compila con éxito con clang 3.5 e imprime
C
Entonces, la pregunta obligatoria es ¿cuál es la correcta? :)
(Sin embargo, me incliné fuertemente hacia la versión clang y la forma msvc de dejar de declarar la variable después de simplemente cambiar el tipo con técnicamente su typedef parece un poco extraño)
fuente
C::C y;
no tiene sentido, ¿verdad? TampocoC::C (y);
Al principio pensé que esta era una instancia de Most-Vexing-Parse stackoverflow.com/questions/tagged/most-vexing-parse , pero ahora creo que es solo un comportamiento indefinido, lo que significa que los tres compiladores son "correctos".C::C
no nombra un tipo, nombra una función, por lo que GCC tiene razón en mi opinión.Respuestas:
GCC es correcto, al menos de acuerdo con las reglas de búsqueda de C ++ 11. 3.4.3.1 [class.qual] / 2 especifica que, si el especificador de nombre anidado es el mismo que el nombre de la clase, se refiere al constructor, no al nombre de la clase inyectada. Da ejemplos:
Parece que MSVC lo malinterpreta como una expresión de conversión de estilo de función creando un temporal
C
cony
como parámetro de constructor; y Clang lo malinterpreta como una declaración de una variable llamaday
de tipoC
.fuente
A::A a;
, los nombres de las funciones deben ignorarse, ¿o no?C (B *)
" .G ++ es correcto ya que da un error. Porque no se puede llamar directamente al constructor en tal formato sin
new
operador. Y aunque su código llamaC::C
, parece una llamada de constructor. Sin embargo, de acuerdo con el estándar 3.4.3.1 de C ++ 11, esta no es una llamada de función legal o un nombre de tipo ( consulte la respuesta de Mike Seymour ).Clang está mal ya que ni siquiera llama a la función correcta.
MSVC es algo razonable, pero aún no sigue el estándar.
fuente
new
cambia el operador?new B(1,2,3)
es una especie de "llamada directa al constructor" (que, por supuesto, no lo es) a diferencia de la instanciación temporalB(1,2,3)
o la declaraciónB b(1,2,3)
.new B(1,2,3)
es?new
, un nombre de tipo y una lista de argumentos del constructor. Todavía no es una "llamada directa al constructor".C::C (y);
comoC::C y;
, es decir, una definición de una variable y de tipo C (usando el tipo inyectado C: : C mientras se ignora erróneamente la especificación del lenguaje cada vez más loco 3.4.1,2 que hace que C :: C sea el constructor). Eso no es un error tan flagrante como parece pensar, en mi opinión.