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

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

这是 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

我认为编译器失败是因为它试图创建一个没有构造函数参数的 ScopeLock 对象(名为 m_),并且正确地识别出唯一的 ScopeLock 构造函数需要一个 pthread_mutex_t& 作为论据——这样正确吗?

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

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

#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 且没有构造函数参数的 std::stringrStrcout 导致 "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);

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

如果有人能证实我的推测是否正确,我将不胜感激。

尝试更换 ScopeLock(m_);ScopeLock s(m_);。该对象需要它的名称。

-

也许编译器将 ScopeLock(m_) 视为 ScopeLock m_。我用clang编译,也出现了类似的错误信息。

以下行编译无任何投诉:

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

Why is line 37 not interpreted as creation of an anonymous ScopeLock object with ctor argument m_?

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

不担心以后处理成声明会不会出错

如果您考虑一下,回退到完全不同的解释会在代码维护期间导致一些非常讨厌的 action-at-a-distance 结果。想象一下,如果你写了这段代码,它被接受为 function-style-cast,后来有人添加了一个默认构造函数...

most vexing parse”的“规范”含义是指对象声明和函数声明之间的歧义。

你的情况是一个不同的歧义:对象声明和 functional-style 转换之间的歧义(更正式地说:显式类型转换的函数符号,见 5.2.3)。后者的歧义已解决,有利于对象声明。因此错误。编译器将您的代码视为简单的

ScopeLock m_;

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

6.8 Ambiguity resolution [stmt.ambig]

1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

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

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

0, ScopeLock(m_);

(ScopeLock(m_));