はじめに
皆さんは競技プログラミングで「テンプレート」は使っていますか?
rep
や chmin
、 modpow
など、よく使う処理を事前に用意したもののことです。
この記事では、C/C++で使える「テンプレート」1の記述方法について紹介します。
C/C++のテンプレートは邪魔!
テンプレートはとても便利です。コンテスト時に書くコードが少なくなり、解答スピードが速くなります。
実装方法を忘れてコンテスト中に調べ直す、なんてこともなくなります。
しかし、便利さを追い求めて様々な関数やクラスの追加を重ねるうちに、テンプレートが膨大な行数になってしまいがちです。
また、C/C++ではどうしても使うより先に宣言が必要です。すると、 main 関数はテンプレートの下に書かなくてはならず、いちばん下の main 関数にたどり着くまでのスクロールでイライラしてしまいます。
さらに、参考にしようとあなたの提出コードを読もうとした人が、いつまでも続くテンプレートを見て読むのを諦めてしまうかもしれません。
ヘッダファイルに分割しようにも、AtCoderなどの競技プログラミングサイトではフォルダでまとめて提出するといったことができません。
なので、すべて1つのファイルに書く必要があります。
ん? ヘッダファイル? 1つのファイルに書く? …………!
必殺!「自身をインクルード」
私、ひらめいちゃいました。
提出ファイルにソースファイル兼ヘッダファイルになってもらって、提出ファイルに提出ファイルをインクルードすればいいのです!
「ちょっと何言ってるかわからない。」
まあ、一度見てみてください。
必殺技の使い方
必殺技を使うには、下のように書きます。
テンプレートの部分は、自分用に書き換えて使ってください。
#ifndef INCLUDED_MAIN
#define INCLUDED_MAIN
#include __FILE__ // このファイル自体をインクルード
int main()
{
// 解答コード
}
#else // INCLUDED_MAIN
// ↓テンプレート↓
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define all(x) begin(x), end(x)
template <class T>
bool chmax(T &a, const T &b) {
if (a < b) a = b; return a < b;
}
template <class T>
bool chmin(T &a, const T &b) {
if (b < a) a = b; return b < a;
}
// ↑テンプレート↑
#endif // INCLUDED_MAIN
main 関数がテンプレートより上に来ています!
これでテンプレートをどれだけ増やしても、ファイルやリンクを開くとすぐに main 関数が読めます!
必殺技の解説
ポイントはプリプロセッサによる条件分岐です。インクルードガードと同じような方法を使っています。
#ifndef INCLUDED_MAIN
// ①
#else
// ②
#endif
最初の行の #ifndef
の条件分岐で、 INCLUDED_MAIN
マクロが定義されていないときは①のコードだけ読まれ、すでに定義されているときは②のコードだけ読まれます。
#define INCLUDED_MAIN
// INCLUDED_MAIN が定義されているので
#include __FILE__ // ←ここでは②だけ読まれる
そして、①の部分の最初に INCLUDED_MAIN
を定義してから、自身をインクルードします。 __FILE__
は自身のファイル名2の文字列リテラルに展開される組み込みマクロです。
すると、先ほど解説した条件分岐で②の部分だけ読まれます。
ということは、①の部分はソースファイル、②の部分はヘッダファイルとして扱えます。
したがって、①の部分に main 関数を、②の部分にテンプレートを書けばいいのです。
これでテンプレートが main 関数の下にあってもコンパイルが通ります!
おわりに
これで問題を解く効率が少しはよくなると思います。3
C/C++使いの方はぜひご活用ください。
コメント