16

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

vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());

我了解 Most Vexing Parse 的大部分细节,以及为什么第二个临时迭代器可以解释为一个返回迭代器且不带参数的函数类型,但我不明白为什么第一个临时迭代器可以是解释为一种类型。它代表什么类型?我的想法是它会是某种函数类型,但我看不出这个名字是如何cin被使用的。是否声明参数是istream_iterator<int>命名的cin?如果是这样,这是否意味着您可以任意给函数的参数名称加上括号?如果是这样,为什么?

4

4 回答 4

12

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

于 2011-08-10T08:29:47.530 回答
8

我是否已经说过我喜欢 Clang(非常喜欢)?

只需尝试以下(简化代码)

#include <vector>

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

int main() {
  std::vector<int> v(int(i), int());
  foo(v);
}

在新改名的LLVM Try Out 中(嗯,它只是从 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.

因此,@john 是对的,int(i)它被解释为int i,即函数的命名参数。

于 2011-08-10T09:24:05.293 回答
6

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

如果参数是函数指针,void (*f)()你需要这样写。

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

于 2011-08-10T08:27:57.607 回答
3

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

所以你去:

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

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

[Example:

struct S {
    S(int);
};

void foo(double a)
{
   S w(int(a));  // function declaration
   S x(int());   // function declaration
   S y((int)a);  // object declaration
   S z = int(a); // object declaration
}
—end example]

2 - 由函数样式转换和类型标识之间的相似性引起的歧义可能出现在不同的上下文中。歧义表现为函数式强制转换表达式和类型声明之间的选择。解决方案是任何在其句法上下文中可能是类型标识的构造都应被视为类型标识。

3- [Example:

#include <cstddef>

char *p;
void *operator new(size_t, int);

void foo() {
    const int x = 63;
    new (int(*p)) int; // new-placement expression
    new (int(*[x]));   // new type-id
}

//4 - For another example,

template <class T>
struct S {
    T *p;
};
S<int()> x;  // type-id
S<int(1)> y; // expression (ill-formed)

//5 - For another example,
void foo()
{
   sizeof(int(1)); // expression
   sizeof(int()); // type-id (ill-formed)
}

//6 - For another example,
void foo()
{
   (int(1)); //expression
   (int())1; //type-id (ill-formed)
}
—end example]

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

[Example:

class C { };
void f(int(C)) { }    // void f(int (*fp)(C c)) { }
                      // not: void f(int C);
int g(C);
void foo() {
    f(1); //error: cannot convert 1 to function pointer
    f(g); //OK
}

//For another example,
class C { };
void h(int *(C[10]));  // void h(int *(*_fp)(C _parm[10]));
                      // not: void h(int *C[10]);

—end example]
于 2011-08-10T10:10:27.367 回答