1

In C++ (and using the standard library facilities), if I want to read an integer from the standard input, I need to do this:

int x;
std::cin >> x;

And looking at the std::istream page on cppreference, it seems there is no function such as std::cin.scan<int>() which returns an int prvalue; nor is there a freestanding function std::scan<int>(std::cin) which does so.

My question is: Why?

It seems nonsensical to me to encourage people to:

  1. Define and name variables they may not need (e.g. as opposed to func_taking_an_int(std::cin.scan<int>()).
  2. Split the definition and the initialization of a variable

So what's the rationale of only supporting the operator form?

Now, I know C++ istreams go back decades, but it's still weird to me (not to mention that an extra method/function could have been added later on).


Motivation: I saw this question and realized that, to our shame, we cannot offer the poster a better way to write their program other than using an uninitialized variable. I was assuming that, surely, we can help that poster bring the definition into the same statement as the initialization... but it seems we cannot.

einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

2

Of course there is one:

#include <iostream>
#include <iterator>

int main() {
  auto const i = *std::istream_iterator<int>(std::cin);
}

Note that this causes UB if std::cin doesn't have a valid int. You might expect it to throw an exception, but since iostreams predate exceptions, that is not how the API works. You should compare it with the end-iterator.

std::istream_iterator<int> it(std::cin), end;
auto const i = (it != end) ? *it : -1;

Finally, you might want to wrap that in a nice, reusable API:

#include <iostream>
#include <iterator>
#include <optional>

template <class T>
auto scan(std::istream& is) -> std::optional<T> {
  std::istream_iterator<int> it(std::cin), end;
  if (it != end) return std::optional(*it);
  return std::nullopt;
}

int main() {
  auto const i = scan<int>(std::cin);
  if (i) {
    return *i;
  }
  return -1;
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • 1
    This is cool, but "there is one" is not the same as "you have to write it yourself." – sweenish May 20 '22 at 18:09
  • 1
    @sweenish the very first snippet is exactly what OP wants, minus error-checking because iostream predates exceptions. And it's in the standard library. – Aykhan Hagverdili May 20 '22 at 18:32
  • `return std::optional(*it);` should be just `return *it;` But frankly, if you are going to write a scanning function that returns an `optional`, I wouldn't bother using `istream_iterator` at all, just check the return state of `operator>>` instead, eg: `template std::optional scan(std::istream& is) { T t; if (std::cin >> t) return t; return std::nullopt; }` – Remy Lebeau May 20 '22 at 19:24
  • Is it really UB? Doesn't it trigger an exception? Also why would I want to use an istream iterator in `scan()` rather than just define a local variable and return it? Relying on NRVO? Regardless, though - the suggestion is irrelevant to the question, in that I'm not asking what _could_ be written, but what's in the standard library. – einpoklum May 20 '22 at 21:30
  • @einpoklum Doesn't `std::istream_iterator(std::cin)` address the salient parts of your question? i.e. no additional variable is needed. – cigien May 20 '22 at 23:22
  • @einpoklum the first and second snippets are using just std facilities, you can use them the way you described in the question. – Aykhan Hagverdili May 21 '22 at 04:11
  • @cigien: Well, I asked about istreams themselves; plus - they don't quite address it, ecause (1.) it's a bit of a "hijacking" of what's intended to be used for iteration, not a single action; (2.) verbose syntax (3.) requires user to be aware and correctly utilize lots of things at the same type (4.) the undefined behavior at end-of-file issue; what's up with that? :-( (5.) I really don't believe the answer to my question is "istreams don't have it because standard library designers expected people to dereference istream_iterators". – einpoklum May 21 '22 at 06:37
  • @einpoklum `std::istream_iterator(std::cin)` is very similar to your `scan(std::cin)` request, isn't it? IOstreams predate exceptions, you couldn't expect it to throw one. Just check for the end iterator, and there you go. – Aykhan Hagverdili May 21 '22 at 07:47
  • @AyxanHaqverdili: `scan()` is an _example_. But what you've just said about iostreams predating exceptions is important - definitely merits being in an answer. – einpoklum May 21 '22 at 07:50