Подтвердить что ты не робот

Отсутствие ошибки компиляции при использовании параметризованного конструктора

Сегодня на работе я наткнулся на поведение в С++, которое я не понимаю. Для иллюстрации моей проблемы я привел следующий пример кода:

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

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

  • Почему мой первый пример компилируется? Почему я не получаю ошибку компиляции?
  • Почему не компилируется код при попытке throw my_ex;?
  • Почему брекеты устраняют проблему?
4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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.

который сразу же укажет вам на проблему.