19

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?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065

4 Answers4

13

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.

john
  • 85,011
  • 4
  • 57
  • 81
  • `istream_iterator(cin)` is NOT the same as `istream_iterator cin`. – Nawaz Aug 10 '11 at 08:33
  • 3
    It is as a parameter to a function. – Bo Persson Aug 10 '11 at 08:36
  • 3
    I think they are in the context of the question asked (perhaps I shouldn't have said _exactly_). Care to elaborate? – john Aug 10 '11 at 08:37
  • @john: `istream_iterator(cin)` is same as `istream_iterator anonymous(cin)`. That is `cin` is an argument to the constructor of `istream_iterator`. – Nawaz Aug 10 '11 at 08:43
  • 2
    @Nawaz: `istream_iterator(cin)` is not being interpreted as an expression but as a parameter declaration, that's the point of the question. – john Aug 10 '11 at 08:48
  • @john: What is that supposed to mean? – Nawaz Aug 10 '11 at 09:00
  • @Nawaz: I mean that the code 'vector v(istream_iterator(cin), istream_iterator());' is interpreted as a function prototype because, in part, `istream_iterator(cin)` can be interpreted as a parameter declaration. Am I missing something here? I wish you'd tell me if I am. – john Aug 10 '11 at 09:02
  • 1
    @Nawaz: I think you could be a little more constructive in your comments. – john Aug 10 '11 at 09:30
  • Or maybe, @Nawaz, **you** are wrong. Have you tested it? What does the compiler believe the variable to be? Simple test: Create a function that takes an `int` as argument, and in `main` have that *vector definition* (actually function declaration), and then try to pass the `v` to the function. The error message will tell you what the type is according to the interpretation of the compiler. – David Rodríguez - dribeas Aug 10 '11 at 09:32
  • @Matthieu: No. He is talking about `istream_iterator(cin)` which is NOT causing the problem. Its `istream_iterator()`which is causing the problem. Your case is different. In your case `int i` is interpreted as parameter declaration, because `i` is not declared an object in the scope. And `int()` is a function pointer type exactly in the same way as `istream_iterator()`. See this : http://www.ideone.com/p1Xx5 ...and this : http://www.ideone.com/d4kep ... and compare them! – Nawaz Aug 10 '11 at 09:37
  • David: See my previous comment to @Matthieu. Especially these : http://www.ideone.com/p1Xx5 .. and http://www.ideone.com/d4kep .. – Nawaz Aug 10 '11 at 09:44
  • 2
    @Nawaz, You misunderstood. I didn't say `istream_iterator(cin)` was causing the problem. The OP specifically asked how `istream_iterator(cin)` could be interpreted as a parameter declaration. So that's the question I answered. – john Aug 10 '11 at 09:47
  • @john: Hmm... I see. Now I understand your point. Thanks for the clarification. :-) – Nawaz Aug 10 '11 at 09:50
  • 1
    @Nawaz: We are aware that the issue is *provoked* by `istream_iterator()`, @john's answer simply say that this line *can* be interpreted as a declaration because `istream_iterator(cin)` can be interpreted as a function parameter *in the context* of a function declaration. I would further note that whether `i` is in scope (or not) has no influence in this case. – Matthieu M. Aug 10 '11 at 09:51
  • @Matthieu: Yeah. I understand the issue now. Thanks for all the clarification. :-) – Nawaz Aug 10 '11 at 09:52
  • @Nawaz: no problem, I don't always get all the questions on first reading either (I blame my English understanding... but most of the times it's just me reading too hastily). – Matthieu M. Aug 10 '11 at 09:55
  • Yes, my answer could have included more context from the OP question. – john Aug 10 '11 at 09:56
  • 1
    @Nawaz you are wrong about "istream_iterator(cin) is same as istream_iterator anonymous(cin)." in the general case too. It would declare `cin`. For example `int(cin);` is equivalent to `int cin;`. – Johannes Schaub - litb Aug 10 '11 at 15:32
  • @Johannes: That actually depends. But I understand that now, knowing the context. :-) – Nawaz Aug 10 '11 at 15:33
  • 3
    @Nawaz, there is no context needed. It's always the declaration of `cin`, it cannot be a temporary object. [You have just been wrong](http://biodork.files.wordpress.com/2011/03/science-youre-doing-it-wrong.jpg). – Johannes Schaub - litb Aug 10 '11 at 15:39
  • @Johannes: It depends on the second parameter. If you use extra parens in that, then `istream_iterator(cin)` would be an anonymous object, and would produce the result as intended. – Nawaz Aug 10 '11 at 15:43
  • @Nawaz there is no "second paramter" in `int(cin);` and also not in `istream_iterator(cin);`. You are somehow underlining the "second parameter" of that particular example in the question, but it has no special role in distinguishing anything. You can just aswell throw parentheses around the first parameter. `vector v((istream_iterator(cin)), istream_iterator());`. Your position is deeply confusing and it looks like despite having quoted a large amount of Standards text, you haven't understood the issue. – Johannes Schaub - litb Aug 10 '11 at 15:51
  • @Johannes: I'm talking about this : `vector v(istream_iterator(cin), istream_iterator());` in which I'm saying if you use extra parens for `istream_iterator()` then, the first one will be an object. – Nawaz Aug 10 '11 at 15:55
  • 1
    @john [Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie) is the inventor of C. – L. F. Mar 28 '19 at 11:24
  • Ken Thompson is creator of B and a militant critic of both C and C++ to this day. – Swift - Friday Pie Jun 06 '21 at 17:35
8

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
6

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.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Can you give an example if where this would be necessary? – templatetypedef Aug 10 '11 at 08:28
  • int *a[10] is an array of 10 int pointers, but int (*a)[10] is a pointer to an array of 10 integers (I hope I got that the right way round). – john Aug 10 '11 at 08:32
  • Ah, so it's not so much "you need it" as much as "some types require it.". I misinterpreted your remark to be "there are types for which it is a compile-time error to not include an extra set of parentheses that would otherwise not be necessary." – templatetypedef Aug 10 '11 at 08:34
5

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]
Nawaz
  • 353,942
  • 115
  • 666
  • 851