(为什么)这是一个令人烦恼的解析示例吗?

(Why) Is this an example of a vexing parse?

本文关键字:烦恼 为什么 一个      更新时间:2023-10-16

这是C++中令人烦恼的解析示例吗?

#include <pthread.h>
#include <iostream>
class ScopeLock
{
public:
ScopeLock(pthread_mutex_t& m)
: mrMutex(m)
{
pthread_mutex_lock(&mrMutex);
}
~ScopeLock()
{
pthread_mutex_unlock(&mrMutex);
}
protected:
pthread_mutex_t& mrMutex;
};
class Foo
{
public:
Foo()
{
pthread_mutex_init(&m_, NULL);
}
~Foo()
{
pthread_mutex_destroy(&m_);
}
void Func()
{
ScopeLock(m_); // Is this a vexing parse?
std::cout << __FUNCTION__ << std::endl;
}
protected:
pthread_mutex_t m_;
};
int main(int argc, char* argv[])
{
Foo foo;
foo.Func();
return 0;
}

输出:

>g++ main.cpp 
main.cpp: In member function u2018void Foo::Func():
main.cpp:37:17: error: no matching function for call to 'ScopeLock::ScopeLock()'
ScopeLock(m_);
^
main.cpp:37:17: note: candidates are:
main.cpp:7:3: note: ScopeLock::ScopeLock(pthread_mutex_t&)
ScopeLock(pthread_mutex_t& m)
^
main.cpp:7:3: note:   candidate expects 1 argument, 0 provided
main.cpp:4:7: note: ScopeLock::ScopeLock(const ScopeLock&)
class ScopeLock
^
main.cpp:4:7: note:   candidate expects 1 argument, 0 provided

我认为编译器失败了,因为它试图创建一个没有 ctor 参数的ScopeLock对象(名为 m_),并正确识别出唯一的ScopeLockctor 将一个pthread_mutex_t&作为参数——这是正确的吗?

不过,为什么这是一个令人烦恼的解析(如果是的话)?为什么第 37 行没有被解释为创建带有 ctor 参数m_的匿名ScopeLock对象?

如果上面是一个令人烦恼的解析示例,为什么下面的不是烦人的解析?

#include <iostream>
#include <string>
class Foo
{
public:
Foo()
{
pStr = "Foo";
}
~Foo()
{
}
void Func()
{
const std::string& rStr = std::string(pStr);
std::cout << rStr << std::endl;
}
protected:
const char* pStr;
};
int main(int argc, char* argv[])
{
Foo foo;
foo.Func();
return 0;
}

编译和输出:

>g++ main.cpp
>./a.out
Foo
>

第二个代码块似乎与第一个代码块非常相似。那么,为什么编译器不将第 18 行视为创建名为pStr的没有 ctor 参数的std::string呢?rStrcout导致"Foo"实际上表明匿名std::string是用const char*参数创建的。

如果有人能在这里阐明,我将不胜感激。谢谢。

更新我刚刚注意到在第一个代码块中更改了这一点:

ScopeLock(m_); // Is this a vexing parse?

对此:

const ScopeLock& rSl = ScopeLock(m_); // Is this a vexing parse?

导致编译通过。因此,将匿名对象转换为右值可以解决令人烦恼的解析问题吗?我不清楚。

另一方面,在第二个代码块中,更改以下内容:

const std::string& rStr = std::string(pStr);

对此:

std::string(pStr);

编译就好了。但是,生成的pStrcout为空。我认为这证实了编译器实际上是在使用默认构造函数创建一个名为pStrstd::string。所以实际上这类似于它在第一个代码块中尝试做的事情。

如果有人能确认我的猜测是否正确,我将不胜感激。

"最烦人的解析"的"规范"含义是指对象声明和函数声明之间的歧义。

在你的例子中,你有一个不同的歧义:对象声明和函数式强制转换之间的歧义(更正式地说:显式类型转换的函数表示法,参见 5.2.3)。后一种歧义被解决,有利于对象声明。因此错误。编译器将代码视为简单

代码
ScopeLock m_;

这使得它抱怨缺少默认构造函数。

6.8 歧义解决

1涉及表达式语句和声明的语法存在歧义:以函数样式显式类型转换 (5.2.3) 作为其最左侧子表达式的表达式语句可能与第一个声明符以 (.在这些情况下,声明是声明。

您是否要将其称为"最令人烦恼的解析"的另一种风格取决于您。

有许多不同的方法可以使编译器将其解释为表达式而不是声明。您也可以这样做

0, ScopeLock(m_);

或作为

(ScopeLock(m_));
为什么第

37 行没有被解释为使用 ctor 参数创建匿名ScopeLock对象m_

这是因为标准有一条规则,如果代码具有可以解释为声明或函数调用的结构,则选择声明处理。

它不担心作为声明的处理是否会导致以后的错误。

如果您考虑一下,回退到完全不同的解释会导致在代码维护期间出现一些非常讨厌的远距离操作结果。 想象一下,如果你写了这段代码,它被接受为函数样式转换,后来有人添加了一个默认构造函数......

尝试替换ScopeLock(m_);ScopeLock s(m_);。对象需要其名称。

-

也许编译器认为ScopeLock(m_)ScopeLock m_.我用 clang 编译,它显示了类似的错误消息。

以下行编译没有任何抱怨:

void Func()
{
ScopeLock(m)(m_);
std::cout << __FUNCTION__ << std::endl;
}