【问题标题】:Most vexing parse with array access最令人头疼的数组访问解析
【发布时间】:2018-05-26 08:02:28
【问题描述】:

在查看一些 C++03 代码时,我发现了一个让我感到困惑的最令人头疼的解析的实例:

#include <sstream>
#include <string>

int main(int, char** argv)
{
    std::stringstream ss(std::string(argv[0]));
}

live example on wandbox

在上面的 sn-p 中,ss 是一个函数声明,它接受 std::string* 并返回 std::stringstream

std::string(argv[0]) 是如何被解析为std::string* 的?

直觉上我认为argv[0] 无疑是对argv 的访问。

【问题讨论】:

  • 这无法使用 Visual Studio 2015 为我编译,因为 argv[0] 显然是 argv 类型的 0 大小数组。如果我使用argv[1],我可以得到和你一样的结果。有趣的问题。
  • 我认为它等同于std::stringstream ss(std::string argv[]);,它本身等同于std::stringstream ss(std::string * argv);
  • 我认为有一个准则,如果它看起来像一个声明,它就是一个。所以就像@FrançoisAndrieux 写你从 std::string 的实例化不是实例化,而是声明 std:strings 数组
  • CppCon 2017: Louis Brandy “Curiously Recurring C++ Bugs at Facebook” 链接设置为正确的时间码。我花了一段时间才想起我在哪个视频中了解到的,但完全符合您刚刚遇到的情况

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


【解决方案1】:

原因是因为在函数声明的上下文中,编译器会将std::string(argv[0])解释为std::string argv[0],即一个零大小数组的声明作为函数参数命名 argv(从main 中覆盖argv,因为这是一个不同的范围),然后它相当于一个 array-to-pointer-decay 的指针。

因此,std::stringstream ss(std::string(argv[0])); 的含义与std::stringstream ss(std::string* argv); 相同

编辑:由于它在 cmets 中得到了正确注释,因此零大小的数组声明在 C++ 中是无效的,从而导致程序格式错误。使用 -pedantic 标志(GCC 和 clang)编译此代码时,将发出警告。 Visual Studio 甚至会产生编译错误。但是,对于任何其他数组索引而不是 0,上述论证仍然成立。

【讨论】:

  • 所以最令人头疼的解析比我一直想的还要邪恶……不仅仅是“如果它看起来像一个函数声明,它就是一个函数声明”,而且“如果它看起来像一个函数声明”无效的函数声明它是一个函数声明”....omfg
  • @tobi303:看看语法。它将数组大致定义为type name_optional [ size_expression ],对于求值为零的表达式没有特殊的语法规则。不过会非常困难,因为 C++ 中的表达式评估是图灵完备的。
  • @tobi303 但它不是无效声明。在 C++ 中,a variable declaration is allowed to wrap the variable name in parenthesis。即int(x)int x 相同。这就是为什么std::string(argv[0]) 在这种情况下可以被视为std::string argv[0]。只需删除 std::string 即可避免 MVP:std::stringstream ss(argv[0]);
  • @RemyLebeau 不是 0 大小的数组无效?
  • @RemyLebeau 是的,明白了。碰巧void foo(double x[0]){} 很好,因为 c++ 的另一个怪癖
【解决方案2】:

我相信这是遵循“声明语法就像表达式语法”的原则,以及“数组”参数是指针的事实。

以下数组声明是等价的:

int x[1];
int (x)[1];
int (x[1]);

或多或少,因为 x[a](x)[a](x[a]) 是等价的表达式。

因此,

std::stringstream ss(std::string(argv[0]))

                 <=>

std::stringstream ss(std::string argv[0])

                 <=>

std::stringstream ss(std::string* argv)

【讨论】:

    猜你喜欢
    • 2020-08-17
    • 2013-06-08
    • 1970-01-01
    • 2019-11-30
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    相关资源
    最近更新 更多