std::cin::get() にはまる

とても簡単に書くと、次のプログラムを実行してCtrl-Dを入力した際に

-1

とだけ出力されて欲しかったのです。

#include <iostream>

int main() {
  while (1) {
    int c = std::cin.get();
    std::cout << c << std::endl;
  }
}

ところが実際にはこうなります。

-1
-1
-1
...(以下続く)...

どうやらEOFだけは特別扱いされて、一度到達するとstd::cin.get()はEOFを返し続けるようです。というのはまぁ、EOFが入力されたら以降の入力は無いと考えるのが普通なので許せます。
ただEOFも単なる1byteの値なわけで、読み飛ばせそうな気がします。実際読み飛ばしてるっぽいプログラムもあるし。
なのでreferenceを見てそれらしいメソッドを探してみます。
http://www.cppreference.com/wiki/io/start
どうやらstd::cin.clear()というものがあるようです。EOFフラグをリセットできると書いてあります。使ってみます。

#include <iostream>

int main() {
  while (1) {
    int c = std::cin.get();
    std::cout << c << std::endl;
    if (std::cin.eof()) {
      std::cin.clear();
    }
  }
}

実行してみます。

-1
-1
-1
...(以下続く)...

ダメです。適当に「eof clear」等でググると、stdinを生で触る場合は次のようにして解決できる事が分かりました。

#include <cstdio>

int main() {
  while (1) {
    int c = getc(stdin);
    printf("%d\n", c);
    if (c == EOF) {
      clearerr(stdin);
    }
  }
}

これで、Ctrl-Dを入力するごとに「-1」と出力されるようになりました。
やりたいことが出来る事は分かったので、「cin clearerr clear」等で適当にググります。
するとこんなページを見つけました。1999の記事です。
http://www.delorie.com/djgpp/bugs/show.cgi?000277
僕と同じ悩みを抱えているようです。素晴らしい事に解決法も書かれていて、std::cin.clear();の後にstd::cin.seekg(0, std::ios::end);と書けば良いようです。
試してみます。

#include <iostream>

int main() {
  while (1) {
    int c = std::cin.get();
    std::cout << c << std::endl;
    if (std::cin.eof()) {
      std::cin.clear();
      std::cin.seekg(0, std::ios::end);
    }
  }
}

今度は期待通りの動作をするようになりました。

ところでlink先の記事ではc++ libraryのバグじゃない?と言われているのですが、10年後の今になって再現性があるので、きっと仕様でしょう。が、なぜstd::cin.get()は素直にEOFを読み飛ばしてくれないのか分かりません。getc(stdin)なら読み飛ばしてくれるのに。