views:

116

answers:

1

This code works:

std::ifstream f(mapFilename.c_str());
std::string s = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
ParseGameState(s);

Whereby mapFilename is an std::string and void ParseGameState(const std::string&);.

And this does not:

std::ifstream f(mapFilename.c_str());
std::string s(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
ParseGameState(s);

This is the error:

game.cpp: In member function ‘int Game::LoadMapFromFile(const std::string&)’:
game.cpp:423: error: no matching function for call to ‘ParseGameState(std::string (&)(std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> > (*)()))’
game.cpp:363: note: candidates are: ParseGameState(const std::string&)

So it seems that it recognizes s as a function declaration and not a variable declaration in this case.

Why is that? Is this a bug in GCC 4.2.1 (Apple build)? Or does GCC handles this correctly? Is this undefined in the C++ standard?

+9  A: 

This is C++'s "most vexing parse." A quick Google for that should turn up lots of hits with lots of details. The basic answer is that yes, the compiler is treating it as a function declaration -- and C++ requires that it do so. There's nothing wrong with your compiler (at least in this respect).

If it's any comfort, you have lots of good company in having run into this. In fact, it's sufficiently common that C++0x is added a new brace-initializer syntax, in large part because it avoids this ambiguity. Using it, you could write something like:

std::string s{std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};

That will make it clear that the contents of the braces are intended to be values for initializing s, not types of parameters to a function named s. I don't know if Apple has a port of it yet, but gcc accepts the new syntax as of version 4.5 (or so).

Edit: Rereading N3092, Johannes is (as usual) quite correct. The applicable language is (§8.5.4/3/5): "If T has an initializer-list constructor, the argument list consists of the initializer list as a single argument; otherwise, the argument list consists of the elements of the initializer list."

So, since std::string has an initializer-list constructor, this would attempt to "stuff" the two istreambuf_iterators into an initializer list, and pass that to the std::string ctor that takes an initializer list -- but that would be a type mismatch, so the code can't compile. For some other type type that (unlike std::string did not have an initializer-list ctor) the transformation above would work (thanks to the "otherwise..." in the quote above). In the case of std::string, you'd have to use one of the current alternatives such as std::string s = std:string(...).

I apologize for the incorrect suggested fix -- in this case, one that's all the worse because it confuses an issue that will probably be excessively confusing on its own, and if anything will need careful clarification, especially over the next few years.

Jerry Coffin
Thanks! [Here on WP](http://en.wikipedia.org/wiki/Most_vexing_parse) is also a good explanation about the most vexing parse.
Albert
It looks like the above `std::string` variable declaration won't work in C++0x: Because `std::string` has an initializer list constructor, the entire `{ ... }` will be taken as a single argument, and will try to initialize the `initializer_list<char>` parameter and fail to compile :(
Johannes Schaub - litb
@Johannes: Yup, I think you're right. I almost wonder if it wouldn't be better if this was reworded so instead of considering a list-initializer and then (only if that wasn't present) consider other ctors, if instead it just built an overload set and picked a best match -- probably with the minor adjustment that creating an initializer_list<T> would be equivalent to no conversion with respect to goodness of match (with the proviso that I've only thought about it a few minutes, so that might cause a subtle (or even glaring) problem that I'm missing).
Jerry Coffin
@Jerry seems like it's already taken care of: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1151
Johannes Schaub - litb
@Johannes: Ah, so it has. Looks like I was a bit late to that party... :-)
Jerry Coffin