この記事は 新歓ブログリレー2021 4日目の記事です。
自己紹介
こんにちは、20B の tatyam です。
普段は競技プログラミングをしていて、AtCoder赤 だったり、AtCoder Beginner Contest を作っていたりします。
はじめに
C++ で競技プログラミングをやっていると、こんな関数があったら良いな〜と思うことがあります。
{}
の要らない可変引数 min / max- 可変引数の入力 / 出力関数
- 引数の個数で機能の変わる rep マクロ
これを、可変引数関数 や 可変引数マクロ を使って作ってみましょう。
なお、この記事では C++17 と using namespace std;
を仮定します。
可変引数関数の作り方
試しに、引数をまとめて tuple で包む可変引数関数を作ってみましょう。
template<class T>
auto f(T a){
return tuple{a};
}
などとすると引数 1 個でさまざまな型に対応した関数を作ることができますが、ここで class
を class...
に、T
を T...
に、a
を a...
にすると...
template<class... T>
auto f(T... a){
return tuple{a...};
}
可変引数関数になりました。
詳しくは https://cpprefjp.github.io/lang/cpp11/variadic_templates.html を見てください。
可変引数 min
C++ 標準の可変引数 min としては
template <class T>
constexpr T min(initializer_list<T> t);
がありますが、これを呼び出すためには min({a, b, c})
と {}
が必要になってやや面倒です。
そこで、 Python のように min(a, b, c)
で呼び出せるようにしましょう。
template<class... T>
constexpr auto min(T... a){
return min(initializer_list{a...});
}
できました。
引数を initializer_list で包んで上の標準の関数を呼び出しています。
このままだと型が揃っている必要があるので、自動的に共通の型に変換するようにしてみましょう。
template<class... T>
constexpr auto min(T... a){
return min(initializer_list<common_type_t<T...>>{a...});
}
common_type 、便利なものがあるものですね〜
可変引数の入力関数
C++ には cin >> a
みたいななんでも入力してくれる便利なやつがありますが、ちょっと長いので可変引数の関数にしてみましょう。
template<class... T>
void input(T&... a){
(cin >> ... >> a);
}
入力を受け取るには参照渡しにしないといけないので、 T&...
←ここに &
を付けます。
畳み込み式 、便利なものがあるものですね〜
可変引数の出力関数
Python には便利な出力関数 print がありますが、 C++ では空白や改行は自分で書かなければなりません。これも可変引数の関数にしてしまいましょう。
void print(){
cout << '\n';
}
template<class T, class... Ts>
void print(const T& a, const Ts&... b){
cout << a;
(cout << ... << (cout << ' ', b));
cout << '\n';
}
まず (cout << ' ', b)
で b の要素それぞれに cout << ' ',
をくっつけ、これを畳み込み式で展開します。
例えば、 b = [1, 2]
のとき、
(cout << ... << (cout << ' ', b))
は、
(cout << (cout << ' ', 1)) << (cout << ' ', 2)
と展開され、
(cout << (cout, 1)) << (cout << ' ', 2)
空白が出力され、
(cout << 1) << (cout << ' ', 2)
カンマ演算子を処理して、
cout << (cout << ' ', 2)
1 が出力され、
cout << (cout, 2)
空白が出力され、
cout << 2
カンマ演算子を処理して、
cout
2 が出力されます。
@raclamusi さんにご指摘をいただきました。ありがとうございます。
https://twitter.com/raclamusi/status/1370260477100445697
可変引数マクロの作り方
試しに、引数をまとめて int 型で宣言し、それぞれに入力するマクロ INT(...)
を作ってみましょう。
#define INT(...) int __VA_ARGS__; input(__VA_ARGS__)
可変引数マクロは、引数を (...)
で受け取って、 __VA_ARGS__
で展開します。
例えば、 INT(a, b);
は、 int a, b; input(a, b);
と展開されます。
詳しくは https://cpprefjp.github.io/lang/cpp11/variadic_macros.html を見てください。
可変引数 rep マクロ
こんな rep マクロが欲しい!
#define rep(a) for(int i = 0; i < a; i++)
#define rep(i, a) for(int i = 0; i < a; i++)
#define rep(i, a, b) for(int i = a; i < b; i++)
#define rep(i, a, b, c) for(int i = a; i < b; i += c)
だけど、同じ名前のマクロは (引数の個数が違っても) 1 つまでなのでできない!
これを可変引数マクロを使ってどうにかしましょう。
まず、それぞれ別の名前で rep マクロを用意します。
#define rep1(a) for(int i = 0; i < a; i++)
#define rep2(i, a) for(int i = 0; i < a; i++)
#define rep3(i, a, b) for(int i = a; i < b; i++)
#define rep4(i, a, b, c) for(int i = a; i < b; i += c)
あとは、引数の個数にしたがってこれらを切り替えれば良いです。
そこで、引数を 5 つ以上受け取って 5 番目を返すマクロ overload4
を用意して、以下のようにします。
#define overload4(a, b, c, d, e, ...) e
#define rep(...) overload4(__VA_ARGS__, rep4, rep3, rep2, rep1)(__VA_ARGS__)
このようにすることで、引数の個数に応じてマクロを切り替えることができます。
例えば、 rep(i, 1, 10)
は、
overload4(i, 1, 10, rep4, rep3, rep2, rep1)(i, 1, 10)
と展開され、
rep3(i, 1, 10)
と展開され、
for(int i = 1; i < 10; i++)
になります。
おわりに
テンプレートを整備して、自分の書きやすいように改造していこう!
改造の極致1 (私のテンプレート 現在 152 行) : https://atcoder.jp/contests/arc113/submissions/20376392
改造の極致2 (新しい言語になって yukicoder に導入される) : http://rsujskf.s602.xrea.com/?cLay