Vasaka
Vasaka

Reputation: 2022

std::ifstream behaves weirdly with temporary std::string() as constructor argument

I try to compile simple program

int main(int argc, char* argv[]) {
    std::ifstream strings(std::string(argv[1]));
    std::string line;
    while (!strings.eof()) {
        strings >> line;
    }
}

and get peculiar error:

error: request for member ‘eof’ in ‘strings’, which is of non-class type ‘std::ifstream(std::string*) {aka std::basic_ifstream<char>(std::basic_string<char>*)}’

if I change to

std::ifstream strings(argv[1]);

all compiles well.

What happens here? Compiler is gcc 4.7 and 4.9.

Upvotes: 0

Views: 122

Answers (1)

Mike Seymour
Mike Seymour

Reputation: 254621

This is sometimes known as the most vexing parse: the declaration of strings is interpreted as a function declaration, taking a pointer (expressed as an array) as its argument. To make this clearer, I'll remove some parentheses which are redundant in a function declaration:

std::ifstream strings(std::string argv[1]);

which, by the ancient rules of array/pointer confusion, is equivalent to

std::ifstream strings(std::string * argv);

which matches the type mentioned in the error message.

Since C++11, you can resolve this using brace-initialisation:

std::ifstream strings{std::string(argv[1])};
                     ^                    ^

In historical dialects, you would need more verbosity:

std::ifstream strings = std::ifstream(std::string(argv[1]).c_str());

Note that, before C++11, you couldn't pass a std::string directly to the constructor, but would need c_str() to get a C-style string.

As you say, you can simply pass argv[1] as the argument in any C++ dialect, avoiding the vexatious parse entirely.

Upvotes: 4

Related Questions