3

I have a code:

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main(int argc, char *argv[]) {
    ifstream file;

    do {
        string filename;
        cout << "Input file name:" << endl;
        cin >> filename;
        file.open(filename, ios::in);
    } while (!file.is_open());

    string content(istreambuf_iterator<char>(file),
                   istreambuf_iterator<char>());

    cout << "Content:\n" << content << endl;

    if (file.is_open()) {
        file.close();
    }

    return 0;
}

To read this file content:

 1 -1  0 -3  0
-2  5  0  0  0
 0  0  4  6  4
-4  0  2  7  0
 0  8  0  0 -5

But it outputs 1 only and a new line.
P.S. I am a nobie and just learn C++ by samples. What do I doing wrong?

Шах
  • 5,849
  • 7
  • 38
  • 79
  • Why are you reading integral values into a `string`? Wouldn't it make more sense to read integer values into an integer container? – PaulMcKenzie Apr 30 '17 at 15:11
  • 1
    No, I want to read it into the `string` as simple lines. As I wrote it is for learning only. – Шах Apr 30 '17 at 15:13
  • First, what is in that file? Are those the actual **characters** in the file, or does the file consist of integers that happen to have those values? It makes a big difference as to what is actually in the file. – PaulMcKenzie Apr 30 '17 at 15:17
  • Just I copied a file content to here. It uses UTF-8 encoding but as I know codes of numbers are same like in the ASCII standard. – Шах Apr 30 '17 at 15:23
  • @NeilButterworth, I have problems with a console encoding so it outputs me a some bred instead of normal warnings. – Шах Apr 30 '17 at 15:25

1 Answers1

8

Wow, that was a sneaky one!

Compiling your code on Coliru produced this warning:

main.cpp:20:29: warning: the address of 'std::__cxx11::string content(std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> > (*)())' will always evaluate as 'true' [-Waddress]

Address? What address?? Well, if you look closely at this parenthesized mess and squint a lot, you'll eventually notice that:

string content(istreambuf_iterator<char>(file),
               istreambuf_iterator<char>());

... is actually... a function declaration! Specifically, a function named content, taking two parameters of type std::istreambuf_iterator<char> and returning an std::string.
And indeed, std::cout << content; takes the address of this function and converts it to a boolean, yielding 1 and a fair warning.

This problem is known as the "most vexing parse". It is one of the reasons to use uniform initialization, as folows:

string content{istreambuf_iterator<char>{file},
               istreambuf_iterator<char>{}};

No more syntactic ambiguity, content is now an actual std::string, and everything works.

But do take a moment to unlearn using namespace std;. Thanks :)

Community
  • 1
  • 1
Quentin
  • 58,778
  • 7
  • 120
  • 175
  • Thank you for detail explanation. I can get that `{}` allows to get a compiler that it's variable. But I can't get why this `string content((istreambuf_iterator(file)), istreambuf_iterator());` works but mine does not? Can you explain yet just a bit, please? – Шах Apr 30 '17 at 15:41
  • I.e. why `(istreambuf_iterator(file))` doesn't equal `istreambuf_iterator(file)`? – Шах Apr 30 '17 at 15:42
  • 1
    @Шах There's no meaning to the parentheses (`foo` is still semantically equivalent to `(foo)`), their only use is to make sure that the *syntax* cannot be a function declaration. This is because such an ambiguous line is defined to be parsed, if possible, as a function declaration first. – Quentin Apr 30 '17 at 15:46
  • Sorry for my poor English... I.e. `int foo(1);` can be interpretated as a function but `int foo((1));` is like a variable in the body of a some function? – Шах Apr 30 '17 at 15:52
  • 1
    @Шах that's the spirit. In your case `int foo(1);` cannot be a function declaration though. The ambiguity arises when there's a type: is `int foo(float(bar));` an `int` initialized with `float(bar)` (`bar` cast to `float`), or a function signature for `int foo(float bar);`? You can wrap a parameter's name in parentheses when declaring it, so it could be both. Hence, the function is chosen. You can't, however, wrap the *whole* parameter, so `int foo((float(bar)));` cannot be a function: it's unambiguously an `int`. – Quentin Apr 30 '17 at 15:57
  • I don't sure that I understood correct and feel self stupid... But seems I got u. Thank you very much! – Шах Apr 30 '17 at 16:03
  • 1
    [Most vexing parse](http://stackoverflow.com/questions/7007817/a-confusing-detail-about-the-most-vexing-parse) – zett42 Apr 30 '17 at 16:06
  • Thanks for a link! I didn't know about it. Now I understood my mistake. – Шах Apr 30 '17 at 16:08
  • 1
    @Шах don't worry, that's just one of the weird corners of C++. Most programmers bump into it sometimes, and they just curse a bit, add some parentheses or braces and move along :) – Quentin Apr 30 '17 at 16:08
  • Yeah, it is mush harder than C# or Java. But at least I understood this error. – Шах Apr 30 '17 at 16:12