似乎语言的歧义性问题总是难以避免的,连C++语言都不可避免。

如果没记错,CFG文法在规约过程是存在歧义的?所以C++也就无法避免的会遇到语法歧义的问题。这个被称为Most Vexing Parse就是C++语言在遇到某一句法歧义时特定的决策方法(翻译自维基百科, 提供英文以防止错译:The most vexing parse is a specific form of syntactic ambiguity resolution in the C++ programming language. )。

在之前写代码的过程中就曾遇到过这个问题: 定义一个拥有默认构造函数的对象,使用一下的方式(不正确的):

ClassName m();

编译器并不会解释m为一个ClassName对象,而是一个 返回ClassName、接收空参数的函数(整句被解释为一个函数声明)。

上述问题已经是很久遇到的了,开始也是迷迷糊糊,发现去掉括号就好使了,后来几经踩坑,才算明白问题的根本原因以及编译器的苦衷。只是,不想今天遇到了更加Vexing的形式。

我要定义一个最小堆,如下:

priority_queue<int, vector<int>, greater<int>> minHeap(greater<int>(), vector<int>());

上述定义没有报错,但是我一调用minHeap.size(); 编译器就报错:

error: request for member 'size' in 'minHeap', which is of non-class type 'std::priority_queue<int, std::vector<int>, std::greater<int> >(std::greater<int> (*)(), std::vector<int> (*)())'
 minHeap.size();

实在太长了,我一时没有看清,直到搜到了爆栈上的相似问题:c++ could not pass argument to std::priority_queue constructor , 看到里面提到的 Most Vexing Parse , 才明白又是这个问题。

即, 上面还是定义了 1 个minHeap 的函数,只不过,它的参数类型都太「奇怪」了: greater<int>() 指定了一个函数类型:入参是空,返回参数是greater<int>; 而 vector<int>() 同理; 是不是太 Vexing 了……

先说说针对这个问题的解决办法,我觉得有3种吧:

  1. 根本不需要传递构造函数参数。

    直接用 priority_queue<int, vector, greater> minHeap; 即可

  2. 给每个参数多加一对括号。

     priority_queue<int, vector<int>, greater<int>> minHeap((greater<int>()), (vector<int>()));
    

    没有查原因,我想因为是小括号优先级高啊,先解释得到匿名对象,然后再解释minHeap,就消除了歧义,其再不能被解释为函数声明了。

  3. 使用C++11的Uniform Initialization Syntax (维基百科中的通用方法)

    即是使用花括号。

     priority_queue<int, vector<int>, greater<int>> minHeap{greater<int>{}, vector<int>{}};
    

    不过使用这个也可能带来歧义,如:

     vector<int>{4};
    

    上述其实是生成了一个包含1个元素的vector,该元素初值为4;而非vector(4), 生成4个初值为0的vector。

    想来,这个方法也不算太值得推荐。

最后,再看Most Vexing Parse。目前我还没有看到中文的翻译。如果没有理解错,感觉可以译为最复杂生成,即按照最让人厌烦的方式分析语句。不过这可能也仅仅表示这种分析是让人Vexing的?犹记得当年英语老师叮嘱ing表示事物的性质,ed表示人的感受,这么来说还是第一种有道理啊。应该是C++制定人员觉得让你瞎写,就给你最复杂的解析,让你懵逼。咳,感觉这个可以作为一个面试C++的题,应该可以分类那些写代码和不写代码的人,哈哈哈。