10

Consider the following program:

#include <fstream>

struct A {};

int main(int argc, char** argv) {
    A a(std::fstream(argv[1]));
}

Clang in C++1y mode reckons that the MVP is invoked such that a is parsed as a function declaration:

clang++ -std=c++1y -O3 -Wall -Wextra -pedantic-errors -pthread main.cpp && ./a.out

main.cpp:6:8: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse]

    A a(std::fstream(argv[1]));

       ^~~~~~~~~~~~~~~~~~~~~~~

main.cpp:6:9: note: add a pair of parentheses to declare a variable

    A a(std::fstream(argv[1]));

        ^

        (                    )

I understand the MVP, but not in this instance: argv[1] is clearly an expression, and there's no type before it, so how could this line be parsed as a function declaration?

Is it that the semantic interpretation on argv[1], that would disambiguate the line as an object declaration, doesn't occur until after the compiler has already chosen to parse the line as a function declaration? Or is it a Clang bug? Or is it perfectly normal through some interpretation of the tokens argv [ 1 ] that I'm missing?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055

2 Answers2

13

I think it's being parsed as

A a(std::fstream argv[1]);

i.e. a function that takes in an array of 1 std::fstream, where the extra parentheses are redundant.

Of course, in reality, that array parameter decays to a pointer, so what you end up with semantically is:

A a(std::fstream* argv);
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 2
    Ah, yes, that's it. I _always_ miss this case! Thanks :) – Lightness Races in Orbit Feb 07 '14 at 10:16
  • Thanks for this but.. does it even make sense to declare a function there in main() ? Why is clang being confused? – Marco A. Feb 07 '14 at 10:21
  • @Jarod42: Or, in fact, `std::fstream* argv` – Lightness Races in Orbit Feb 07 '14 at 10:21
  • @DavidKernin Block-scope declarations of functions are perfectly valid and have been since before the first standards of C and of C++. –  Feb 07 '14 at 10:21
  • @LightnessRacesinOrbit: it was most to correct the wording: "function that takes in an array of **1** std::fstream". – Jarod42 Feb 07 '14 at 10:23
  • Feel free to correct the wording if it bothers you. – user541686 Feb 07 '14 at 10:24
  • @LightnessRacesinOrbit Generally speaking, agreed, but I have seen (very old) C code where each function declares all other (non-standard) functions it uses locally, instead of using header files in the project. I can imagine that it made sense at the time, if the local declarations caused a measurable increase in compilation speed. –  Feb 07 '14 at 10:25
  • 2
    -1. this part is wrong >> `a function that takes in an array of 1 std::fstream`. The parameter is NOT an array, it is a pointer! – Nawaz Feb 07 '14 at 10:26
  • @Nawaz: Sheesh, if it bothers you *that much* then why don't you just fix it to your liking, like I'd already invited you to do? I wrote it that way because that was the only somewhat-logical wording I could think of with the number "1" in it, which is part of the signature. I didn't even downvote you for calling parentheses "braces", but if we're nitpicking so much maybe I should have... – user541686 Feb 07 '14 at 10:29
  • @Mehrdad: *"Sheesh, if it bothers you that much then why don't you just fix it to your liking, like I'd already invited you to do?"* ... Ask yourself. Anyway, what you said, is NOT just a matter of vocabulary; **it is plain wrong fundamentally**. :-) – Nawaz Feb 07 '14 at 10:36
  • 1
    @Nawaz: "Ask yourself"...? I'm asking you, it's not me whom it's bothering. Way to destroy a fine answer by nitpicking... – user541686 Feb 07 '14 at 10:38
  • @Mehrdad: So you think, `T a[1];` and `T *a;` are same? Nothing wrong with that? You think the difference is pointless? How can it be pointless especially when one of them is a pointer? :P – Nawaz Feb 07 '14 at 10:40
  • @Nawaz: No, one of them is a pointer and the other is an array of size 1. In this case the parameter was declared to be an array of size 1. – user541686 Feb 07 '14 at 10:41
  • @Mehrdad: The parameter is NOT an array. Period. (Anyway, I give up). – Nawaz Feb 07 '14 at 10:42
  • @Nawaz: Uh, would you enlighten me as to when when it *would* be an array, in your opinion? Don't give up so easily, you've already dragged me into this so you might as well take it till the end. – user541686 Feb 07 '14 at 10:44
  • @Mehrdad: A parameter can NEVER be an array. It can at most be a *reference* to an array, e.g : `A a(std::fstream (&argv)[1]);`. Here `argv` is *a reference* to an array of size 1. – Nawaz Feb 07 '14 at 10:47
  • 3
    @Nawaz: A parameter can be an array (it's just a placeholder and you can declare it as such, no problem), but an argument can't (since an argument is a value, and values get passed around, but arrays can't be passed around). – user541686 Feb 07 '14 at 10:48
  • @LightnessRacesinOrbit: Thanks for saving the day. :-) – user541686 Feb 07 '14 at 10:48
  • @Mehrdad: *A parameter can, an argument can't.*. SHOW ME! – Nawaz Feb 07 '14 at 10:50
  • @Mehrdad: No, seriously, show me that *"A parameter can, an argument can't"*. I know what you say does NOT make sense! – Nawaz Feb 07 '14 at 10:53
  • @Nawaz The whole point of the decay is to convert an *array parameter* to a *pointer argument*. I think Mehrdad is correct here. – Bartek Banachewicz Feb 07 '14 at 11:09
  • 4
    @Nawaz: _"`[C++11: 8.3.5/5]:` After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively."_ This means that the parameter starts off as `T[N]`, is changed to `T*`, and after that the argument can of course only take the type `T*` because that's what the parameter type has become. – Lightness Races in Orbit Feb 07 '14 at 11:12
  • @BartekBanachewicz: Yes. If it decays, how can it be an array? It is like saying "alive people are buried by certain christians and muslims." No they're NOT alive. They're dead. Likewise, parameter type can never an array; if you declare it an array, it WILL decay **in order to** become the parameter type! – Nawaz Feb 07 '14 at 11:24
  • @LightnessRacesinOrbit: So what is the parameter type? pointer or array? My answer is : "pointer". Your answer seems to be "array, but well, it then decays into a pointer, so it is an array and then pointer ... and pointer but it was an array anyway" :P. The point is simple: the compilers may do whole lots of stuffs before giving your the final product, it is their job; the intermediate things are NOT what the type is! For the programmers, the parameter type is : pointer! – Nawaz Feb 07 '14 at 11:29
  • 3
    @Nawaz: No, my answer is precisely what I told you, and what the standard says. It's fruitless debating with you because you're debating with the standard, so I shall stop now. – Lightness Races in Orbit Feb 07 '14 at 11:32
  • @LightnessRacesinOrbit: I said that Mehrdad's statement *"a function **that takes** in an array of 1 std::fstream"* is wrong. Do you dispute that? – Nawaz Feb 07 '14 at 11:33
  • @Nawaz: Honestly, just, stop. I clarified his answer and that's the end of it. – Lightness Races in Orbit Feb 07 '14 at 11:37
  • @LightnessRacesinOrbit: Alright. *"a function **that takes** in an array of 1 std::fstream"*. ;-) – Nawaz Feb 07 '14 at 11:43
3

The parens are superfluous. All of the following are the declarations:

T a;
T (a);
T ((a));
T (((a))));

so are these:

T a[1];
T (a[1]);
T ((a[1]));
T (((a[1]))));

So is this:

 A a(std::fstream(argv[1]));

which is same as:

 A a(std::fstream argv[1]);

which is same as:

 A a(std::fstream *argv);

Hope that helps.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • These are not braces. – user541686 Feb 07 '14 at 10:17
  • These are not braces, and none of those invoke the MVP because you don't name a type for that hypothetical single parameter. I'd even go so far as to say that this property of parentheses _in this context_ isn't proof of much, since argument expressions could easily have different rules from parameter lists. – Lightness Races in Orbit Feb 07 '14 at 10:17
  • I'm not saying the declaration of `T a;` invokes MVP. That is used to explain the superfluous role of parens! – Nawaz Feb 07 '14 at 10:20
  • I understand, but since argument lists could behave differently from parameter lists, it doesn't really proove anything about the question. Even though, as it turns out, they _don't_ behave differently ;) I'm just being picky. – Lightness Races in Orbit Feb 07 '14 at 10:21
  • @LightnessRacesinOrbit: I'm NOT saying it is proof. Please don't read what I don't say :P... the purpose of the various declarations is to demonstrate the fact that the behavior *extra parens* is same even in simple declarations! ... and again it is said, because simple declarations are easy to remember, so it helps me (or programmers like me) to understand complex declarations. – Nawaz Feb 07 '14 at 10:22