(为什么)这是一个令人烦恼的解析的例子吗?
(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::string
? rStr
的 cout
导致 "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
是空的。我认为这证实了编译器实际上正在使用默认构造函数创建一个名为 pStr
的 std::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_));
这是 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::string
? rStr
的 cout
导致 "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
是空的。我认为这证实了编译器实际上正在使用默认构造函数创建一个名为 pStr
的 std::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 argumentm_
?
这是因为标准有一个规则,如果代码具有可以解释为声明或函数调用的结构,则选择作为声明处理。
不担心以后处理成声明会不会出错
如果您考虑一下,回退到完全不同的解释会在代码维护期间导致一些非常讨厌的 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_));