A confusing detail about the Most Vexing Parse

2019-01-02 21:18发布

My question is how the following line can be parsed as a function declaration:

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

I understand most of the details of the Most Vexing Parse and why the second temporary iterator can be interpreted as a type that is a function returning an iterator and taking no arguments, but what I don't get is why the first temporary iterator can be interpreted as a type. What type does it represent? My thought is that it would be some sort of function type, but I can't see how the name cin gets used. Is it declaring that the parameter is an istream_iterator<int> named cin? If so, does that mean that you can arbitrarily parenthesize the names of arguments to functions? And if so, why?

4条回答
看淡一切
2楼-- · 2019-01-02 21:49

istream_iterator<int>(cin) is exactly the same as istream_iterator<int> cin but with superfluous parens. This declarator syntax was inherited from C, and I think even the inventor of C (Ken Thompson?) described it as a mistake.

查看更多
忆尘夕之涩
3楼-- · 2019-01-02 22:00

Did I already said that I liked Clang (a lot) ?

Just try the following (simplified code)

#include <vector>

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

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

In the newly rebrandished LLVM Try Out (well, it just went from llvm-gcc to clang).

And you get:

/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.

And therefore, @john is right, int(i) is interpreted as int i, ie a named parameter to the function.

查看更多
像晚风撩人
4楼-- · 2019-01-02 22:05

There is a section called Ambiguity resolution in the Standard (2003) which is dedicated to such syntaxes. I think I don't need to explain it further if you read the section yourself, for its very clear with lots of examples!

So here you go:

8.2 Ambiguity resolution [dcl.ambig.res]

1 - The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]

[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 - The ambiguity arising from the similarity between a function-style cast and a type-id can occur in different contexts. The ambiguity appears as a choice between a function-style cast expression and a declaration of a type. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a 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 - Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses. In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a simple-type-specifier rather than a 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]
查看更多
浅入江南
5楼-- · 2019-01-02 22:06

Yes, it is the parameter name. And, yes you can add a set of parenthesis, because sometimes you have to.

If the parameter is a function pointer, void (*f)() you need to write it like that.

The people writing the standard have not spent their precious time pointing out exactly the cases where the parenthesis are allowed or actually required, so the standard just says that you can have them.

查看更多
登录 后发表回答