noyのブログ

健康なエンジニアを目指す人のブログ

(クソコード) 変数宣言と同時に標準入力を受け取りたい [C++編]

得られる情報はないです。

二つに分かれる

C++ で標準入力をするなら、どのようなコードを書きますか?
cinを使うのであれば、

int a;
cin >> a;

このように書けば良いですね。基礎中の基礎です。

時々、

int a; cin >> a;

のように、改行しない一行のコードを見ることがあります。気持ちはわかります。

どうにかして、変数の宣言/入力をまとめてできないでしょうか。

in()でもできない

cinしてその値を返すだけの関数を作ってみるとどうでしょうか。

int in(){
  int x;
  cin >> x;
  return x;
}

これを使えば変数宣言から入力までをまとめて行えます。
in関数で普通にcinしてますが

int a = in();

問題点は 型がintでなければ使えない ことです。

template<typename T> T in(){
  T x;
  cin >> x;
  return x;
}

テンプレートを使うことで型の問題は解消されますが、残念なことにタイプ数が増えます。

int a = in<int>();
double b = in<double>();

各型についてin関数を作るという方法もありますが、それはなんかいやなのでしません。 どうにかして型名を書くのを避けられないでしょうか?

そしてクソコードへ

class Void{ public: Void() { } }
const Void IN;

class Int{
private:
  int value;
public:
  Int() { }
  Int(Void IN){
    int x;
    cin >> x;
    value = x;
  }
}

int main(){
  Int a(IN);
}

いろいろなものを犠牲にすることで宣言と入力をまとめて行えるようになりました。 入力部分のみに着目すると、タイプ数を3文字(全体の23%)削減することができます。

各型ごとにクラスが必要になるから手間が増えただけ

このままだとvalueにアクセスする手段がないので(publicにしたりget()を作ってもいいですが)、以下の変換関数をクラスに加えておきます。

operator int() const { return value; }

すると、あたかもintであるかのように扱えるようになります。
参考:https://msdn.microsoft.com/ja-jp/library/wwywka61.aspx

  Int a(IN);
  cout << a << endl; // OK
  cout << a + 10 << endl; // OK

それができたからといってどうにもならないですが。

クイズ

突然ですがクイズです。以下の文はどのように動くでしょうか。

vector<Int> a(5, IN);

正解は、「一度だけ入力を受け取り、その値を使って全ての要素を初期化する」でした。

vectorもまとめたい

vectorの宣言と同時に複数の入力を受け取る、ということも実現したかったのですがその方法が思いつきませんでした。

in関数を使えばできます。ただし、型名を書く必要はありますが。

以下の入力を受け取るとします。全てintに収まる整数です。

N
A_1 A_2 ... A_N
template<typename T> T in(){
  T x;
  cin >> x;
  return x;
}
template<typename T> vector<T> in(int n){
  vector<T> x(n);
  for(int i = 0; i < n; i++) x[i] = in();
  return x;
}

int main(){
  auto a(in<int>(in<int>()))
}

かなり奇妙に感じるコードになりました。 in関数が奇妙に感じるのは当然としても、autoがコンストラクタでも型推論をしてくれるのは意外でした。

vector<int> a = {1, 2, 3};
auto b = a; // <- わかる
auto c(a); // <- !?

冷静になって考えてみると、奇妙の原因は何をするのかわからないin関数が入れ子になっていることのような気がします。

議論

今のままではできることが減ったintでしかありません。メンバ関数を増やして機能を補いましょう。
結局各型ごとに、例えば Int, Double, String クラス等を作る羽目になりますが、BaseTypeみたいなクラスを作って継承すれば手間は少なくなります。

本当はある部分に変数名を書くだけで入力までしてくれる、というものを考えていたんですが、思いつかないのでやめました。

あとC++編とありますが別の編はないです。
もしかすると言語によってそういうのできるのかなぁ……

結論

普通に書いて。