这两个版本的代码有什么区别?

时间:2019-02-21 15:46:11

标签: c++ c++11 most-vexing-parse

此代码导致编译错误(最令人讨厌的解析)

#include <iostream>

class A {
        int a;
public:
        A(int x) :a(x) {}
};

class B {
public:
        B(const A& obj) { std::cout << "B\n";}
        void foo() {std::cout << "foo\n";}
};

int main()
{
        int test = 20;
        B var(A(test));      //most vexing parse
        var.foo();
        return 0;
}

但是,如果我通过20而不是testA(20)而不是A(test)),则不会出现编译错误。

#include <iostream>

class A {
        int a;
public:
        A(int x) :a(x) {}
};

class B {
public:
        B(const A& obj) { std::cout << "B\n";}
        void foo() {std::cout << "foo\n";}
};

int main()
{
        int test = 20;
        //B var(A(test));
        B var(A(20));            //ok works fine
        var.foo();
        return 0;
}

为什么这不是最令人讨厌的解析?这两个代码版本有什么区别?

2 个答案:

答案 0 :(得分:10)

可以像这样定义变量

type(name)

因此

B var(A(test)); 

声明一个名为var的函数,该函数返回一个B并接受一个名为A的{​​{1}}。在

test

如果尝试执行相同的操作,则B var(A(20)); 参数将称为A,这不是有效的变量名。由于它不能是变量的名称,因此我们知道它是一个值,而我们正在构造一个值为20的类型为var的变量B

答案 1 :(得分:5)

最令人头疼的问题是语法问题,而不是语义问题。在语法上,A(test)简化为identifier : OPEN_PAREN : identifier : CLOSE_PAREN。在上下文中,这是模棱两可的,因为第二个标识符可以是变量名或类型名。编译器必须选择一种解释这种令牌序列的方法,而且没有一个是错误的。

相比之下,A(20)可以归结为identifier : OPEN_PAREN : integer_literal : CLOSE_PAREN。整数文字不能解释为标识符,因此无法将其解释为类型名。因此,必须将其解析为用于初始化A类型的对象的表达式。