Search code examples
c++buffer

std::ws vs. std::skipws in C++


I'll start with a short piece of code to explain my question:

#include <iostream>
#include <string>

int main(){
    int val;
    string s;
    
    std::cin >> val;
    std::getline(std::cin >> std::ws, s);

    std::cout << val << s << std::endl;

    return 0;
}

I understand that using std::cin >> val will leave a newline character in the buffer.
If std::getline(std::cin, s) is used afterwards, it's simply going to skip the input because it'll capture that whitespace from cin, but if we do std::getline(std::cin >> std::ws, s), it should work as expected as the std::ws is going to consume all the leading whitespace.

Now my question is, there's also such a thing as std::skipws which should be doing the same thing from what I understand (std::getline(std::cin >> std::skipws, s);), but that is either incorrect because using it still skips the input, or I am using it wrong.

  • Why does std::skipws not work in this case?
  • What's the difference between std::ws and std::skipws?
  • When should we use std::skipws over std::ws?
  • Why do we have 2 implementations of the same thing? Both are supposed to get rid of whitespaces (as per my current understanding)

Solution

  • std::skipws (and std::noskipws) only apply to formatted input, ie operator>>, which uses the stream's sentry class (the sentry is the one doing the actual skipping). They have no effect on unformatted input, like std::getline(), which don't use the sentry. They set/clear the stream's inner skipws flag, which stays in effect for all subsequent formatted reads until explicitly changed, that flag is not reset after each read, like some other flags are. Each time the sentry is created during a formatted read, it looks at the current skipws flag and acts accordingly.

    std::ws unconditionally reads whitespace from an input stream. It is used on a per-read basis only. It is ideal for use with unformatted input when you do want to skip whitespace sometimes, but not always.