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

競技プログラミングと音ゲーをしています。 AtCoder銀冠 ICPC 2020/21 Yokohama 3 位, WF 15 位 (good_yamikin) ICPC 2021/22 Yokohama 3 位 (tonosama) ICPC 2022/23 Yokohama 1 位 (tonosama)

この記事をシェア

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

関連する記事

2021年3月19日
traPグラフィック班の活動紹介
NABE icon NABE
2023年4月27日
Vulkanのデバイスドライバを自作してみた
kegra icon kegra
2021年4月2日
DXライブラリで重力パズルゲームを作る
Macky1_2 icon Macky1_2
2023年7月13日
アルゴリズム班はやとき王選手権「競(けい)プロ」を開催しました!
abap34 icon abap34
2021年4月18日
ベズー係数とN項の拡張ユークリッドの互除法
0214sh7 icon 0214sh7
2023年4月29日
CPCTF2023 PPC作問陣 Writeup
noya2 icon noya2
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記