feature image

2021年3月12日 | ブログ記事

【C++】可変引数関数と可変引数マクロで便利なテンプレートを作ろう【新歓ブログリレー4日目】

この記事は 新歓ブログリレー2021 4日目の記事です。

自己紹介

こんにちは、20B の tatyam です。
普段は競技プログラミングをしていて、AtCoder だったり、AtCoder Beginner Contest を作っていたりします。

みんなも traP に入って競技プログラミングをしよう!

はじめに

C++ で競技プログラミングをやっていると、こんな関数があったら良いな〜と思うことがあります。

これを、可変引数関数可変引数マクロ を使って作ってみましょう。

なお、この記事では C++17 と using namespace std; を仮定します。

可変引数関数の作り方

試しに、引数をまとめて tuple で包む可変引数関数を作ってみましょう。

template<class T>
auto f(T a){
    return tuple{a};
}

などとすると引数 1 個でさまざまな型に対応した関数を作ることができますが、ここで classclass... に、TT... に、aa... にすると...

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

明日の担当者は mazrean, Hmcmch さんです、お楽しみに!

tatyam icon
この記事を書いた人
tatyam

競技プログラミングと素数大富豪と音ゲーをしています。

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2021年3月19日
traPグラフィック班の活動紹介
NABE icon NABE
2021年4月2日
DXライブラリで重力パズルゲームを作る
Macky1_2 icon Macky1_2
2021年4月18日
ベズー係数とN項の拡張ユークリッドの互除法
0214sh7 icon 0214sh7
2018年12月12日
多重スリーブの世界と,各種進捗報告。
Silviase icon Silviase
2021年4月26日
CPCTF2021 作問者writeup by hukuda222
hukuda222 icon hukuda222
2021年4月25日
CPCTF2021 作問者writeup by xxpoxx
xxpoxx icon xxpoxx
記事一覧 タグ一覧 Google アナリティクスについて