2016-11-23 3 views
18

Сегодня на работе я столкнулся с поведением в C++, которое я не понимаю. Я произвел следующий пример кода, чтобы проиллюстрировать мою проблему:Отсутствие ошибки компиляции при использовании параметризованного конструктора

#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; 
} 

Этот фрагмент кода компилируется без каких-либо ошибок и производит следующий вывод:

$ 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. 

Обратите внимание, что для my_ex конструктор я определил не было называется. Далее, если я хочу на самом деле бросить эту переменную:

throw my_ex; 

я получаю ошибку компиляции:

$ 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 

Если добавить фигурные скобки вокруг преобразования, как это:

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; 

Тогда он работает, как я и ожидал:

$ 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) 

У меня есть следующие вопросы:

  1. Почему мой первый пример скомпилирован? Почему я не получаю ошибку компиляции?
  2. Почему не код компилируется, когда я пытаюсь throw my_ex;?
  3. Почему брекеты устраняют проблему?

ответ

34

Согласно most vexing parse, MyException my_ex(std::string(text)); - это объявление функции; функция называется my_ex, взяв параметр с именем text с типом std::string, возвращает MyException. Это не определение объекта вообще, тогда конструктор не будет вызван.

Примечание сообщение об ошибке undefined reference to 'my_ex(std::string)' для throw my_ex; (вы пытаетесь бросить указатель на функцию на самом деле), что означает, что не может найти определение функции my_ex.

Чтобы исправить это, Вы можете добавить дополнительные скобки (как показал) или использовать braces, который поддерживается с C++ 11:

MyException my_ex1((std::string(text))); 
MyException my_ex2{std::string(text)}; 
MyException my_ex3{std::string{text}}; 
+9

Это, вероятно, самый беспричинный причуда C++. –

+1

@ GillBates Особенно в таком сложном сценарии. – songyuanyao

+1

Спасибо, я не знал этого синтаксиса, отсюда мое недоумение в ситуации. –

4

Ответ заключается в использовании {} (рамно-INIT) так же, как возможное. Иногда, однако, это может быть пропущено непреднамеренно. К счастью, компиляторы (например, лязг, при каких дополнительных флагов предупреждений) может намекнуть:

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. 

, который сразу же указать вам вопрос.

Смежные вопросы