【问题标题】:Absence of compilation error when using parametrized constructor使用参数化构造函数时不存在编译错误
【发布时间】:2016-11-23 14:54:44
【问题描述】:

今天在工作中,我遇到了一个我不理解的 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;
}

这段代码 sn-p 编译没有任何错误,并产生以下输出:

 $ 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. 为什么大括号可以解决问题?

【问题讨论】:

    标签: c++ most-vexing-parse


    【解决方案1】:

    根据most vexing parseMyException my_ex(std::string(text));是一个函数声明;该函数名为my_ex,采用text 类型为std::string 的参数,返回MyException。根本不是对象定义,所以不会调用构造函数。

    注意throw my_ex;的错误信息undefined reference to 'my_ex(std::string)'(实际上你是在尝试抛出一个函数指针),这意味着找不到函数my_ex的定义。

    要修复它,您可以添加额外的括号(如您所示)或使用 C++11 支持的 braces

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

    【讨论】:

    • 这可能是 C++ 最令人气愤的怪癖了。
    • @GillBates 特别是在如此复杂的场景中。
    • 谢谢,我不知道这个语法,所以我对这种情况感到困惑。
    • 因为这个怪癖我总是尽可能使用大括号初始化。
    • MyException ex{ {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.
    

    这将立即指出您的问题。

    【讨论】:

      猜你喜欢
      • 2015-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-31
      • 1970-01-01
      相关资源
      最近更新 更多