#Pythonみたいなprint関数をC++で書きたい!
###はじめに
C++を3週間勉強して、基本的な文法は少しずつ分かってきました。
あとは慣れだと思うので、ひたすらコードを書いて練習しています。
Pythonでは配列の中身をprint
関数で表示できて便利ですが、これをC++で再現してみました。
###Pythonのprint関数
a = 1
print("Test")
print(a, 3.14, "Hello")
print()
arr = [1, 2, 3, 2]
str_arr = ["Hello", "World"]
print(*arr)
print(*str_arr)
print()
table = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for arr in table:
print(*arr)
上記を実行すると以下のように出力されます。
Test
1 3.14 Hello
1 2 3 2
Hello World
1 2 3
4 5 6
7 8 9
複数の要素や配列を引数にとり、同様の出力を行う関数をC++で実装します。
配列はvector
を使うことにします。
###C++での実装
#include <bits/stdc++.h>
using namespace std;
void print() {
cout << endl;
}
template <class Head, class... Tail>
void print(Head&& head, Tail&&... tail) {
cout << head;
if (sizeof...(tail) != 0) cout << " ";
print(forward<Tail>(tail)...);
}
template <class T>
void print(vector<T> &vec) {
for (auto& a : vec) {
cout << a;
if (&a != &vec.back()) cout << " ";
}
cout << endl;
}
template <class T>
void print(vector<vector<T>> &df) {
for (auto& vec : df) {
print(vec);
}
}
int main() {
int a = 1;
print("Test");
print(a, 3.14, "Hello");
print();
vector<int> arr{1, 2, 3, 2};
vector<string> str_arr{"Hello", "World"};
print(arr);
print(str_arr);
print();
vector<vector<int>> table{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(table);
}
上記を実行すると以下のように出力されます。
Test
1 3.14 Hello
1 2 3 2
Hello World
1 2 3
4 5 6
7 8 9
###コードの説明
- 関数のオーバーロード
C++では引数の型が異なる同名の関数を定義することができます。
これを関数のオーバーロードと呼びます。
複数の要素が渡されたときと、配列が渡されたときで、別の処理を書くことができます。
void print() //引数が空の場合
void print(Head&& head, Tail&&... tail) //引数が1個以上の要素の場合
void print(vector<T> &vec) //引数が配列(vector)の場合
void print(vector<vector<T>> &df) //引数が二重配列の場合
- 関数テンプレート
引数の要素はint
型やdouble
型などさまざまですが、要素を表示する処理は同じです。
テンプレートを使うことで、それぞれに対する処理をまとめて書くことができます。
template <class T> //テンプレートを使うことでvector<int>型やvector<double>型などへの処理を
void print(vector<T> &vec) //この中にまとめて書くことができる
template <class T>
void print(vector<vector<T>> &df) //vector<vector<int>>などの二重配列が引数の場合の処理
- 可変引数テンプレート
C++11から任意の個数の引数に対応できるテンプレートの書式が追加されました。
この機能を使うことで、複数の要素を渡すことができます。
template <class Head, class... Tail> //省略記号 ... を書くと可変引数テンプレートになる
void print(Head&& head, Tail&&... tail) {
cout << head; //最初の要素を出力
if (sizeof...(tail) != 0) cout << " "; //表示すべき要素が残っていればスペースで区切る
print(forward<Tail>(tail)...); //最初の要素以外を引数としてprintを呼び出し再帰的に処理
}
- ユニバーサル参照
再帰的な処理を行うところで、forwardを使ってユニバーサル参照にしています。
参照を使わない場合は以下のようなコードになります。
template <class Head, class... Tail>
void print(Head head, Tail... tail) {
cout << head;
if (sizeof...(tail) != 0) cout << " ";
print(tail...);
}
- 範囲for文
C++11ではコンテナの要素を以下の書式で順番に呼び出して処理することができます。
また要素に対して型推論auto
を使うことでシンプルに記述ができます。
for (auto& a : vec) {
cout << a; //配列のそれぞれの要素をcoutで順番に出力
if (&a != &vec.back()) cout << " "; //表示すべき要素が残っていればスペースで区切る
}
for (auto& vec : df) {
print(vec); //二重配列のそれぞれの要素(配列)に対してprint関数を呼び出し
}
###おわりに
上記のprint
関数をコピペして使うと、競技プログラミングで出力したりデバッグするときも便利です。
例えば前回のABC137 B問題では以下のように記述できます。
int main() {
int a, b;
cin >> a >> b;
vector<int> arr;
for (int i = 0; i < 2 * a - 1; ++i) {
arr.push_back(b - a + 1 + i);
}
print(arr);
}
一生懸命勉強して書きましたがまだ修行中の身なので、正確ではない記載があるかもしれません。
コメントやご指摘いただければ幸いです!
###参考文献
C++日本語リファレンス 可変引数テンプレート
C++日本語リファレンス 右辺値参照
C++日本語リファレンス 範囲for文