关于最令人烦恼的解析的令人困惑的细节

tem*_*def 15 c++ most-vexing-parse

我的问题是如何将以下行解析为函数声明:

vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());
Run Code Online (Sandbox Code Playgroud)

我理解Most Vexing Parse的大部分细节以及为什么第二个临时迭代器可以被解释为一个返回迭代器并且不带参数的函数的类型,但是我没有得到的是为什么第一个临时迭代器可以是解释为一种类型.它代表什么类型?我的想法是它会是某种函数类型,但我看不出名称是如何cin被使用的.它是否声明该参数是一个istream_iterator<int>名为cin?如果是这样,这是否意味着您可以任意地将函数的参数名称括起来?如果是这样,为什么?

joh*_*ohn 12

istream_iterator<int>(cin)istream_iterator<int> cin与多余的parens 完全一样.这个声明符语法是从C继承的,我认为即使C的发明者(Ken Thompson?)也将其描述为错误.

  • 它是一个函数的参数. (3认同)
  • 我认为他们是在问的问题的背景下(也许我不应该说_exactly_).注意详细说明? (3认同)
  • @Nawaz,没有上下文需要.它始终是`cin`的声明,它不能是一个临时对象.[你刚才错了](http://biodork.files.wordpress.com/2011/03/science-youre-doing-it-wrong.jpg). (3认同)
  • @Nawaz:“ istream_iterator &lt;int&gt;(cin)”不会被解释为表达式,而是作为参数声明,这才是问题的关键。 (2认同)
  • @Nawaz,你误解了.我没有说'istream_iterator <int>(cin)`导致了这个问题.OP特别询问`istream_iterator <int>(cin)`如何被解释为参数声明.这就是我回答的问题. (2认同)

Mat*_* M. 8

我是否已经说过我喜欢Clang(很多)?

试试下面的内容(简化代码)

#include <vector>

void foo(std::vector<int>);

int main() {
  std::vector<int> v(int(i), int());
  foo(v);
}
Run Code Online (Sandbox Code Playgroud)

在新重新命名的LLVM试用版中(好吧,它刚刚从llvm-gcc转到clang).

你得到:

/tmp/webcompile/_21483_0.cc:6:21: warning: parentheses were disambiguated
                                           as a function declarator
  std::vector<int> v(int(i), int());
                    ^~~~~~~~~~~~~~~
/tmp/webcompile/_21483_0.cc:7:3: error: no matching function for call to 'foo'
  foo(v);
  ^~~
/tmp/webcompile/_21483_0.cc:3:6: note: candidate function not viable:
     no known conversion from 'std::vector<int> (int, int (*)())'
     to 'std::vector<int>' for 1st argument
void foo(std::vector<int>);
     ^
3 diagnostics generated.
Run Code Online (Sandbox Code Playgroud)

因此,@ john是正确的,int(i)被解释为int i,即函数的命名参数.


Bo *_*son 6

是的,它是参数名称.并且,是的,您可以添加一组括号,因为有时您必须这样做.

如果参数是函数指针,则void (*f)()需要像这样写.

编写标准的人没有花费宝贵的时间精确地指出允许或实际需要括号的情况,因此标准只是说你可以拥有它们.


Naw*_*waz 5

Ambiguity resolution标准 (2003) 中有一个章节专门讨论此类语法。我想如果您自己阅读该部分,我不需要进一步解释,因为它非常清楚,有很多例子!

\n\n

所以你开始吧:

\n\n
\n

8.2 歧义消解 [dcl.ambig.res]

\n\n

1 - 由于函数样式转换和 6.8 中提到的声明之间的相似性而产生的歧义也可能出现在声明的上下文中。在这种情况下,需要在参数名称周围带有一组冗余括号的函数声明和带有函数样式强制转换作为初始值设定项的对象声明之间进行选择。正如 6.8 中提到的歧义一样,决议将考虑任何可能是声明的构造。[注意:声明可以通过非函数风格的强制转换、通过 = 来指示初始化或通过删除参数名称周围的冗余括号来显式消除歧义。]

\n
\n\n
[Example:\n\nstruct S {\n    S(int);\n};\n\nvoid foo(double a)\n{\n   S w(int(a));  // function declaration\n   S x(int());   // function declaration\n   S y((int)a);  // object declaration\n   S z = int(a); // object declaration\n}\n\xe2\x80\x94end example]\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

2 - 由于函数样式转换和类型 ID 之间的相似性而产生的歧义可能会出现在不同的上下文中。这种歧义表现为函数式转换表达式和类型声明之间的选择。解决方案是,任何在其语法上下文中可能是类型 ID 的构造都应被视为类型 ID。

\n
\n\n
3- [Example:\n\n#include <cstddef>\n\nchar *p;\nvoid *operator new(size_t, int);\n\nvoid foo() {\n    const int x = 63;\n    new (int(*p)) int; // new-placement expression\n    new (int(*[x]));   // new type-id\n}\n\n//4 - For another example,\n\ntemplate <class T>\nstruct S {\n    T *p;\n};\nS<int()> x;  // type-id\nS<int(1)> y; // expression (ill-formed)\n\n//5 - For another example,\nvoid foo()\n{\n   sizeof(int(1)); // expression\n   sizeof(int()); // type-id (ill-formed)\n}\n\n//6 - For another example,\nvoid foo()\n{\n   (int(1)); //expression\n   (int())1; //type-id (ill-formed)\n}\n\xe2\x80\x94end example]\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

7 - 当类型名称嵌套在括号中时,函数声明的参数声明子句或作为 sizeof 或 typeid 运算符的操作数的 type-id 中会出现另一个歧义。在这种情况下,可以在函数指针类型的参数声明和在声明符 id 周围带有多余括号的参数声明之间进行选择。解决方案是将类型名称视为简单类型说明符而不是声明符 ID。

\n
\n\n
[Example:\n\nclass C { };\nvoid f(int(C)) { }    // void f(int (*fp)(C c)) { }\n                      // not: void f(int C);\nint g(C);\nvoid foo() {\n    f(1); //error: cannot convert 1 to function pointer\n    f(g); //OK\n}\n\n//For another example,\nclass C { };\nvoid h(int *(C[10]));  // void h(int *(*_fp)(C _parm[10]));\n                      // not: void h(int *C[10]);\n\n\xe2\x80\x94end example]\n
Run Code Online (Sandbox Code Playgroud)\n