c++ most-vexing-parse

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:

  1. ¿Por qué compila mi primer ejemplo? ¿Cómo es que no obtengo un error de compilación?
  2. ¿ Por qué no se compila el código cuando intento throw my_ex; ?
  3. ¿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.