了解'most vexing parse' - 为什么允许歧义语法?

Understanding 'most vexing parse' - why allow ambiguous syntax?

本文关键字:歧义 许歧义 语法 为什么 vexing most parse 了解      更新时间:2023-10-16

在试图理解C/C++中的"最令人烦恼的解析"问题时,这个问题立刻浮现在脑海中——为什么要从语法开始就导致这个问题?

例如,

class Timer
{
public:
    Timer();
};
class TimeKeeper
{
public:
    TimeKeeper(const Timer& t);
    int get_time()
    {
        return 1;
    }
};
int main()
{
    TimeKeeper time_keeper(Timer());
    // the above is eq to this: TimeKeeper time_keeper(Timer (*)());
}

那么,为什么不简单地禁止TimeKeeper time_keeper(Timer())是采用未命名函数ptr返回类型Timer的函数声明呢?TimeKeeper time_keeper(Timer (*)())作为函数声明符是劣的吗?

难道不是因为这种语法,我们甚至会出现这种歧义,还是我遗漏了什么?

EDIT:就我个人而言,我从未使用过TimeKeeper time_keeper(Timer())作为函数声明。我一直使用Timer (*)()来指定函数指针,因为我发现它更清晰。

那么,为什么不简单地禁止TimeKeeper time_keeper(Timer())是一个接受未命名函数ptr返回类型Timer的函数声明呢?

假设这个函数声明被调用了一段时间,因为它使用了未命名的参数。如果是这样,那么以下声明也将被禁止:

int max(int,int);  //error (in hypothetical C++)
int min(int,int);  //error (in hypothetical C++)

然后程序员将被强迫在声明中写入参数名称

int max(int a,int b);  //ok 
int min(int a,int b);  //ok 

但是,其他人会站起来问:"当它不使用参数名称时,为什么我被迫在声明中写入参数名称?为什么它不是可选的?"

我认为这家伙很理性,他所要求的是有道理的。强制程序员在声明中命名参数确实是不合理的。

--

阅读您的评论,您似乎认为以下声明完全相同:

int max(Timer());
int max(Timer(*)());

没有。从语法的角度来看,它们并不完全相同,尽管从behavior的角度来看它们完全相同。

微妙的区别在于,在前者中,参数类型是一个不带任何内容的函数,并返回Timer,而在后者中,参数类别是一个指向不带任何信息的函数的指针,并返回Timer。你看到区别了吗?

但问题是,为什么他们的行为同样明智?答案是,在前一个声明中,参数类型是调整的,然后变成指针类型,因此它的行为与第二个声明相同。

C++03标准在§13.1/3 中规定

不同之处仅在于参数声明是函数类型另一个是指向相同函数类型的指针是等价的。也就是说,函数类型被调整为指向函数的指针类型(8.3.5)。

我希望它在C++11中也是一样的。

--

您的疑问(摘自评论):

仍然没有更深入地理解为什么我们需要2语法?

因为它们是两种不同的类型。函数类型,以及指向函数类型的指针。仅作为参数类型,它们的行为是相同的。否则,它们就不一样了。看看我的答案,看看他们在哪里表现不同:

  • 函数语法参考-带和不带&

由于它们在其他情况下表现不同,我们有它们,我们需要。标准不(也不应该)禁止一种语法,因为作为参数类型,它们的行为是相同的。

请注意。。。你听说过:)

C++11通过引入统一初始化语法专门解决了这个问题。现在您可以编写TimeKeeper time_keeper{Timer{}};,并且不存在解析歧义。