c++ - Ausencia de error de compilación al utilizar constructor parametrizado
most-vexing-parse (2)
Hoy en el trabajo me encontré con un comportamiento en C ++ que no entiendo. He producido el siguiente código de ejemplo para ilustrar mi problema:
#include <string>
#include <iostream>
class MyException
{
public:
MyException(std::string s1) {std::cout << "MyException constructor, s1: " << s1 << std::endl;}
};
int main(){
const char * text = "exception text";
std::cout << "Creating MyException object using std::string(const char *)." << std::endl;
MyException my_ex(std::string(text));
std::cout << "MyException object created." << std::endl;
//throw my_ex;
std::string string_text("exception text");
std::cout << "Creating MyException object using std::string." << std::endl;
MyException my_ex2(string_text);
std::cout << "MyException object created." << std::endl;
// throw my_ex2;
return 0;
}
Este fragmento de código se compila sin errores y produce el siguiente resultado:
$ g++ main.cpp
$ ./a.out
Creating MyException object using std::string(const char *).
MyException object created.
Creating MyException object using std::string.
MyException constructor, s1: exception text
MyException object created.
Tenga en cuenta que para my_ex
no se ha llamado al constructor que he definido. A continuación, si realmente quiero lanzar esta variable:
throw my_ex;
Me sale un error de compilación:
$ g++ main.cpp
/tmp/ccpWitl8.o: In function `main'':
main.cpp:(.text+0x55): undefined reference to `my_ex(std::string)''
collect2: error: ld returned 1 exit status
Si agrego llaves alrededor de la conversión, así:
const char * text = "exception text";
std::cout << "Creating MyException object using std::string(const char *)." << std::endl;
MyException my_ex((std::string(text)));
std::cout << "MyException object created." << std::endl;
throw my_ex;
Entonces funciona como hubiera esperado:
$ g++ main.cpp
$ ./a.out
Creating MyException object using std::string(const char *).
MyException constructor, s1: exception text
MyException object created.
terminate called after throwing an instance of ''MyException''
Aborted (core dumped)
Tengo las siguientes preguntas:
- ¿Por qué compila mi primer ejemplo? ¿Cómo es que no obtengo un error de compilación?
- ¿ Por qué no se compila el código cuando intento
throw my_ex;
? - ¿Por qué las llaves resuelven el problema?
De acuerdo con el análisis más MyException my_ex(std::string(text));
, MyException my_ex(std::string(text));
es una declaracion de funcion; la función se llama my_ex
, tomando un parámetro llamado text
con tipo std::string
, devuelve MyException
. No es una definición de objeto, entonces no se llamará a ningún constructor.
Tenga en cuenta que la undefined reference to ''my_ex(std::string)''
mensaje de error undefined reference to ''my_ex(std::string)''
para throw my_ex;
(de hecho, está intentando lanzar un puntero de función), lo que significa que no puede encontrar la definición de la función my_ex
.
Para solucionarlo, puede agregar paréntesis adicionales (como lo ha mostrado) o usar braces compatibles con C ++ 11:
MyException my_ex1((std::string(text)));
MyException my_ex2{std::string(text)};
MyException my_ex3{std::string{text}};
La respuesta es usar {}
(braced-init) tanto como sea posible. A veces, sin embargo, se puede pasar inadvertidamente. Afortunadamente, los compiladores (como clang, sin banderas de advertencia adicionales) pueden insinuar:
warning: parentheses were disambiguated as a function declaration [-Wvexing-parse]
MyException my_ex(std::string(text));
^~~~~~~~~~~~~~~~~~~
test.cpp:13:23: note: add a pair of parentheses to declare a variable
MyException my_ex(std::string(text));
^
( )
1 warning generated.
lo que inmediatamente le señalará el problema.