22

While looking at some C++03 code, I found an instance of the most vexing parse that confused me:

#include <sstream>
#include <string>

int main(int, char** argv)
{
    std::stringstream ss(std::string(argv[0]));
}

live example on wandbox

In the snippet above, ss is a declaration to a function that takes a std::string* and returns std::stringstream.

How is std::string(argv[0]) being parsed as std::string*?

Intuitively I thought that argv[0] was unambiguously an access to argv.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • This failed to compile for me with Visual Studio 2015 because `argv[0]` is apparently a 0 sized array of type `argv`. I can get the same result as you if I use `argv[1]`. Interesting question. – François Andrieux Dec 12 '17 at 15:22
  • 4
    I think it's equivalent to `std::stringstream ss(std::string argv[]);` which is itself equivalent to `std::stringstream ss(std::string * argv);`. – François Andrieux Dec 12 '17 at 15:24
  • I think there was this one guideline, _if it looks like a declaration it is one_. so like @FrançoisAndrieux wrote your instantiation from std::string isn't a instantiation but instead a declaration for array of std:strings – ExOfDe Dec 12 '17 at 18:24
  • [CppCon 2017: Louis Brandy “Curiously Recurring C++ Bugs at Facebook”](https://youtu.be/lkgszkPnV8g?t=30m22s) The links is set to the correct time code. took me while to remember in which video i learned about that but perfectly fits what you have just encountered – ExOfDe Dec 12 '17 at 21:55

2 Answers2

18

The reason is because in the context of a function declaration, the compiler will interpret std::string(argv[0]) as std::string argv[0], i.e. a declaration of a zero-sized array as the function parameter named argv (overshadowing the argv from main, as this is a different scope), which then is equivalent to a pointer by array-to-pointer-decay.

Therefore, std::stringstream ss(std::string(argv[0])); means the same as std::stringstream ss(std::string* argv);

Edit: As it got correctly annotaded in the comments, zero-sized array declarations are invalid in C++, rendering the program ill-formed. When compiling this code with -pedantic flags (GCC and clang), warnings will be issued. Visual Studio even produces a compilation error. For any other array index than 0, the argumentation above however still holds.

Jodocus
  • 7,493
  • 1
  • 29
  • 45
  • 2
    so the most vexing parse is even more evil than I always thought... it is not just "if it looks like a function declaration it is a function declaration" but also "if it looks like an invalid function declaration it is a function declaration"....omfg – 463035818_is_not_an_ai Dec 12 '17 at 15:28
  • 2
    @tobi303: Have a peek at the grammar. It defines arrays roughly as `type name_optional [ size_expression ]`, and there is no special grammar rule for expressions that evaluate to zero. Would be _very_ hard, though, as expression evaluation in C++ is Turing-complete. – MSalters Dec 12 '17 at 15:30
  • 1
    @tobi303 it is not an invalid declaration, though. In C++, [a variable declaration is allowed to wrap the variable name in parenthesis](https://stackoverflow.com/questions/29675601/). Ie `int(x)` is the same as `int x`. This is why `std::string(argv[0])` can be seen as `std::string argv[0]` in this context. Just remove `std::string` to avoid the MVP: `std::stringstream ss(argv[0]);` – Remy Lebeau Dec 12 '17 at 15:58
  • 1
    @RemyLebeau arent 0 sized arrays invalid? – 463035818_is_not_an_ai Dec 12 '17 at 16:41
  • @tobi303 yes, but that doesn't change the fact that MVP would still parse it as a fixed-sized array (even if the size is illegal), and a fixed-sized array decays to a pointer. – Remy Lebeau Dec 12 '17 at 20:09
  • 1
    @RemyLebeau yes, got it. It just happens that `void foo(double x[0]){}` is fine, due to just another quirk of c++ – 463035818_is_not_an_ai Dec 12 '17 at 20:14
7

I believe this follows from the "declaration syntax is like expression syntax" principle, and the fact that "array" parameters are pointers.

The following array declarations are equivalent:

int x[1];
int (x)[1];
int (x[1]);

more or less because x[a], (x)[a], and (x[a]) are equivalent expressions.

Thus,

std::stringstream ss(std::string(argv[0]))

                 <=>

std::stringstream ss(std::string argv[0])

                 <=>

std::stringstream ss(std::string* argv)
molbdnilo
  • 64,751
  • 3
  • 43
  • 82