31

Every time I do a quick snippet of C++ code line

std::string s;
cin >> s;

I curse myself because I forgot it stops at the whitespace rather than getting an entire line.

Then, on remembering getline, I invariably become confused as to the two varieties:

std::string s;
getline (std::cin, s);

and:

char cs[256];
std::cin.getline (cs, sizeof (cs));

Is there actually a difference between these two other than the data type?

It seems to me the C++ way should be the former. Under what circumstances would I use the latter, given that I probably should be using real strings instead of null-terminated character arrays anyway?

And, since input should really be the purview of the input streams, why isn't the former part of istream?

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953

6 Answers6

31

The global getline() function works with C++ std::string objects.

The istream::getline() methods work with "classic" C strings (pointers to char).

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • So, if the only thing it inputs is strings, why isn't it `s.getline (std::cin)` instead of breaking my lovely `object.method()` view of the world? – paxdiablo Feb 02 '11 at 09:00
  • 4
    @paxdiablo, it might be a question of semantics: `getline()` is a stream operation, not a string operation, so it would be strange to make it a method of the `std::string` class. – Frédéric Hamidi Feb 02 '11 at 09:03
  • 6
    My guess is doesn't know about , therefore std::cin can't have method that returns std::string. – Grozz Feb 02 '11 at 09:12
  • 1
    @Grozz, indeed, the two classes are not coupled. – Frédéric Hamidi Feb 02 '11 at 09:13
  • 1
    "why isn't it `s.getline (std::cin)`" -- because making the implementation of `std::string` depend on `std::istream` would be even worse than v.v. "breaking my lovely object.method() view of the world" -- look up "leaky abstraction" and "multiple dispatch". That view of *software* was broken from the get-go. – Jim Balter Sep 30 '17 at 02:46
12

Bear in mind that the Standard library is composed from 3 (main) parts: IOStream, String and STL, plus some tossed in goodies and the C-headers.

I don't see anything weird in having those parts loosely coupled (though I wish it was not the case).

Other incongruities include: std::string::length vs std::string::size, the latter having been added for interface compatibility with the STL and the former having been retained for compatibility with older code.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 3
    I think the phrase is "loosely coupled", "poorly integrated" has negative connotations. I think that it is a good thing (tm) that you can use `std::string` without needing `std::istream` and use `std::istream` without needing `std::string`. – CB Bailey Feb 02 '11 at 09:32
  • 2
    @Charles: would you believe me if I told you that I made the exact opposite change before hitting "submit" :) ? I would personally have favored a more "uniform" standard library. The C++ standard library is the epitome of "organic growth". – Matthieu M. Feb 02 '11 at 10:30
  • 2
    @Charles: I definitely think that it's wrong that iostreams and string aren't coupled. I mean, iostream depends on manipulating strings, at a basic level. Reducing dependencies is one thing, providing member functions that work with C-string is inexcusable if you're not the string class. – Puppy Feb 02 '11 at 10:42
  • 1
    @Puppy: I'd definitely disagree that iostream depends on manipulating strings, but would prefer if they were more tightly integrated. – Mooing Duck Jul 15 '14 at 23:53
9

It's a common interface-design problem. cin.getline() is a natural way to make the request, but to avoid making the stream code dependent on <string>, no cin.getline(std::string&) function can be offered. The free-standing getline(cin, s) can later be added once strings have been brought into scope. Not a problem for char* as there's nothing to #include - all part of the language anyway.

In some ways, it's nice when languages allow later code to add further functions to existing classes (e.g. Ruby), but in other ways the delocalisation bites and, well, compromises maintainability. Then of course there's the popular argument for minimal member functions and lots of free-standing functions: I personally think it shouldn't be taken so far as to make the interface less intuitive and expressive, but each to their own.

Michael Dorst
  • 8,210
  • 11
  • 44
  • 71
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • 1
    This seems to be the only answer that states explicitly why this decoupling is by design. The mere fact that `string` is newer than `iostream` is, after all, not in itself a sufficient explanation for why the latter wasn't retrofitted. I assume there was a time where the decision was less controversial, and the overhead of having an unused `string` class was relatively bigger. – Daniel Saner Mar 15 '17 at 16:32
2

The getline variant inside the iostreams library does not support strings as targets, so the string library defined a variant that does.

Simon Richter
  • 28,572
  • 1
  • 42
  • 64
1

Yes the modern C++ way is to use the free function and input an std::string.

But IOStream has a far longer history (the standard version is at least the third incarnation of the same design) than std::string and that history explain why things are the way they are.

(The getline member has the advantage that it doesn't imply dynamic allocation; that characteristic can be handy at time but would probably not be enough to justify it in a from scratch design).

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
-1

Source:https://www.reddit.com/r/learnprogramming/comments/4fx64h/is_there_a_difference_between_cingetline_and/

The member function cin.getline() works with C strings (i.e. arrays of char) whereas the free function std::getline() works with C++ strings (i.e. std::string.) You should not be using C strings at all when learning C++, which means you should not be using cin.getline().

std::getline() reads a string from an input stream up to some delimiter. The delimiter by default is '\n' and there's no reason to specify that third argument if you're just going to pass the newline character. It doesn't know or care what is contained in that string, other than checking for the delimiter. If you want to attempt to parse that string as an integer or a floating point value you can certainly do that after reading from the stream, but that's not std::getline()'s job. It just reads a string.

  • When citing whole passages verbatim, providing a link to the source is not enough. You should also denote the quotation via the block quote formatting. – anothernode Jun 13 '18 at 13:01