C++中最烦人的解析 —— Most Vexing Parse 完全解析
如果你问我最烦C++什么?
我的回答一定是:most vexing parse(最烦人的解析)

它到底是什么?
“Most Vexing Parse”是 C++ 社区对C++中一种经典语法歧义的称呼,它被 Scott Meyers 在《Effective C++》中正式命名。
先来看一段“天真”的代码
#include <iostream>
class Widget {
public:
Widget() {
std::cout << "Widget 默认构造函数被调用!" << std::endl;
}
~Widget() {
std::cout << "Widget 析构函数被调用!" << std::endl;
}
};
int main() {
Widget w();
return 0;
}
我们想要做什么?
在main函数中,我想创建一个Widget类对象w,并使用默认构造函数初始化。
于是我们很自然地写下了这行看似没问题的代码:
Widget w();
运行结果却让人崩溃
构造函数没被调用,析构函数也没被调用,什么都没输出!
对象呢?对象去哪儿了???
真相:最烦人的解析(Most Vexing Parse)
核心问题出在这一行代码上。
C++ 中有一条非常重要的规则,叫做“优先函数声明规则”(as-if rule / declaration优先):
任何可以被解释为函数声明的东西,编译器都会优先把它解释为函数声明。
这是一条铁律。
于是 Widget w(); 在编译器眼里根本不是创建一个对象,而是:
// 这是一个函数声明!
// 返回类型:Widget
// 函数名:w
// 参数列表:空参数
// (在函数作用域内声明了一个函数,啥也没干)
Widget w();
这个坑其实很常见
相信大多数C++程序员都曾经踩过类似的雷:
std::ifstream ifs("file.txt");
你以为打开了文件,其实你只是声明了一个函数 ifs,返回类型是 std::ifstream,接受一个 const char* 参数……
文件?根本没打开!
那到底该怎么正确创建对象?
最简单、最清晰、永远不会出错的方式:
Widget w;
从C++11开始,强烈推荐使用统一初始化语法(大括号初始化),意图更明确:
Widget w{};
或者更直白一点:
Widget w = Widget();
总结一句话
在C++里,当你想声明一个对象却不小心写成了函数声明时,
编译器永远会选择相信你是在声明函数。
最后问一句:
你觉得这样的C++……到底烦不烦人?


