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

时间:2011-08-10 08:23:45

标签: c++ most-vexing-parse

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

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

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

4 个答案:

答案 0 :(得分:12)

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

答案 1 :(得分:7)

我是否已经说过我喜欢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,即函数的命名参数。

答案 2 :(得分:6)

是的,它是参数名称。并且,是的,你可以添加一组括号,因为有时你必须。

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

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

答案 3 :(得分:2)

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

所以你走了:

  

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

     

1 - 函数式转换与6.8中提到的声明之间的相似性引起的歧义也可能出现在声明的上下文中。 在该上下文中,选择是在参数名称周围的冗余括号集的函数声明和具有函数样式转换作为初始化程序的对象声明之间。正如6.8中提到的含糊不清一样,解决方案是考虑任何可能是声明声明的构造。 [注意:声明可以通过非函数式转换明确消除歧义,用a表示初始化或删除参数名称周围的冗余括号。 ]

[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 - 函数式转换和type-id之间的相似性引起的歧义可能发生在不同的上下文中。歧义似乎是函数式转换表达式和类型声明之间的选择。 解决方案是,任何可能在其语法上下文中都是type-id的构造都应被视为type-id。

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运算符的操作数的type-id中出现另一个歧义。 在这种情况下,选择是在函数类型指针的参数声明和声明器id周围带有冗余括号的参数声明之间。决议是将类型名称视为a   simple-type-specifier而不是declarator-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]