で、結局 #include <bits/stdc++.h> って何?


hakatashiです。

競プロ界では一般的なテクニックであるにも関わらず、何かと定期的に燃えがちな話題であるbits/stdc++.h。便利ではあるけど、そもそもこのファイルって何?年齢は?性別は?家族構成は?彼女はいる?調べてみました!

そもそも、bits/stdc++.hって何?

GNUが開発するC++の標準ライブラリの実装であるlibstdc++のプリコンパイル済みヘッダを生成するためのソースファイルです。

具体的な中身はgccのソースリポジトリのlibstdc++-v3/include/precompiled/stdc++.hから参照することができます。見ると、CおよびC++の標準ライブラリに含まれるヘッダがすべてインクルードされており、このことから「#include <bits/stdc++.h>と書くとすべての標準ライブラリを一度にインクルードできる」という性質を持ちます。

使用例
#include <bits/stdc++.h>

int32_t main() {
  std::vector<int64_t> v = {0, 100, 20000};
  std::cout << v[1] << std::endl; //=> 100
  return 0;
}

タイプ数が少なく、C++のどの機能がどのヘッダにあるかなどを調べなくとも済むことから、競プロなどの界隈ではしばしば用いられるテクニックです。

なお「bits/stdc++.hを使ってはいけない」とする論調で「bits/stdc++.hはGCCが内部で使用しているファイルなので外部から参照してはいけない」と説明されることがありますが、これは誤りです。bits/stdc++.hの使用法に関してはlibstdc++のドキュメントの Precompiled Headers の項に説明があります。

There are three base header files that are provided. They can be used to precompile the standard headers and extensions into binary files that may then be used to speed up compilations that use these headers.

  • stdc++.h
    • Includes all standard headers. Actual content varies depending on language dialect.
  • stdtr1c++.h
    • Includes all of <stdc++.h>, and adds all the TR1 headers.
  • extc++.h
    • Includes all of <stdc++.h>, and adds all the Extension headers (and in C++98 mode also adds all the TR1 headers by including all of <stdtr1c++.h>).

“provided”とあるのでlibstdc++が提供する機能の1つであると見るのが適切でしょう。

正しい用法は? プリコンパイル済みヘッダって何?

上のドキュメントに記されている通り、このヘッダファイルの目的は、ユーザーがlibstdc++の機能を全て含む単一のプリコンパイル済みヘッダを生成することにあります。

プリコンパイル済みヘッダとは、特に巨大なヘッダファイルについて、インクルードしたファイルのコンパイル時間を短縮するためにあらかじめ何らかの形でバイナリの形に落とし込んだヘッダファイルのことを指します。

これは例えば<iostream><string>など通常の標準ライブラリについて個別にプリコンパイルして読み込むことも考えられますが、このような標準ライブラリのインクルードが多いプログラムにおいて真にファイルアクセスを最適化するならば、標準ライブラリのファイルを1つのファイルにまとめて一括で読み込むようにしよう、という発想は自然です。bits/stdc++.hはこの目的で提供されます。

よって正しい使い方は以下のとおりです。まずbits/stdc++.hをプリコンパイルしてstdc++.h.gchを生成します。

$ g++ /usr/include/x86_64-linux-gnu/c++/7/bits/stdc++.h -o stdc++.h.gch

これを使用するには、通常と同じようにヘッダファイルをインクルードするだけで十分です。.gchは必要ないことに注意してください。

$ cat test.cpp
#include <stdc++.h>

int32_t main() {
  std::vector<int64_t> v = {0, 100, 20000};
  std::cout << v[1] << std::endl;
  return 0;
}

$ g++ -I. test.cpp -o test

$ ./test
100

なおコンパイル後のstdc++.h.gchのサイズは数十MB~100MBほどになります。

「bits/stdc++.hを読み込むことによってコンパイルが遅くなる」という説明がされることがありますが、これも必ずしも正しいとは言いがたいです。そもそも上の通りstdc++.hの目的はプリコンパイル済みヘッダを生成することによりコンパイル時間を短縮することにあり、大量のインクルード宣言を含むファイルを (プリコンパイル済みの) bits/stdc++.h に置き換えた場合、理論上逆にコンパイルが早くなることが考えられます。

参考: #include <bits/stdc++.h> コンパイル高速化 - OLIETの自由帳

逆に言えば、このようなプリコンパイルによる最適化を考慮せずにbits/stdc++.hを用いることは本来の目的からは外れていると言えるでしょう。

でも環境依存って聞いたよ?

bits/stdc++.hはlibstdc++固有の機能であり、Clangや CL.exe (VC++) のような他のコンパイラの標準ライブラリには同様のファイルは存在しません。

よって仮想Linux環境なしのWindowsや、自分のことをGCCだと思っている精神異常ClangがインストールされているMacでは通常このテクニックを使用することはできません。

これらlibstdc++以外のライブラリがこのような機能を提供しない理由については推測の域を出ませんが、そもそもこのような標準ライブラリのプリコンパイルという文化が根付いていないことが大きな原因でないかと考えます。

ある程度環境に依存せず動作することが期待されるC++においてこのような非標準の機能を用いることは大きなディスアドバンテージとなります。これは上のようにプリコンパイル無しでは際立った性能の向上が見られないことも相まって「競プロ以外ではbits/stdc++.hを用いるべきでない」とする説の主要な根拠となっています。

※ちなみにVC++の標準ライブラリには__msvc_all_public_headers.hppという似たような機能を提供するヘッダが存在します。こちらはプリコンパイル済みヘッダの生成ではなくライブラリのテストが目的のようで、わざわざ「このヘッダを直接インクルードするな」とコメントに書いてあります。

bitsって何?

libstdc++のディストリビューションパッケージのbitsディレクトリには、stdc++.hの他に多くのヘッダファイルが含まれています。

このディレクトリについて、公式のマニュアルには以下のように記述されています。

  • include/bits
    • Files included by standard headers and by other files in the bits directory.

即ち、主にlibstdc++の標準ライブラリから内部的に参照されるファイルが収められるディレクトリであるようです。いわばutility的な役割のファイル群であると言えるでしょう。このディレクトリがなぜbitsという名前を与えられたのかはわかりませんが、StackOverflowでは「他のファイルを構成するための些末な小片 (bit) の集合であるため」という推測が挙げられています。

bits/stdc++.hはなぜ作られた?

GCCのソースリポジトリによれば、stdc++.h2003年3月14日に Benjamin Kosnik によってチェックインされました。完全に余談ですが彼はGNUの主要なコントリビューターでありながら現在ではデジタルアーティストとしての活動に専念しているという異色の経歴を持つエンジニアです。すごい。

そしてこのコミットに対応するパッチは次の通りです。

Archiving only. This implements a globbed C++ include, stdc++.h, that
includes all the C++ standard includes.

この変更で同時にテストスートや標準ライブラリのコードが変更されていることからも分かる通り、当初の意図はあくまで内部的に用いることが目的だったようです。

で、結局bits/stdc++.hは使っていいの? いけないの?

競技プログラミングにおいて、どのようにコードを書いてはよい、書いていけないなどと論じることはナンセンスでしょう。競技中にジャッジを通るようなプログラムを書くためならあらゆる行為が許されますし、実際にbits/stdc++.hを用いることはプログラムを早く書く上で利する点が多いように感じます (個人的にはGCCよりClangを好むためあまり使う機会がありませんが)。

それ以外の用途において、bits/stdc++.hを用いることは避けるべきだと考えます。これには以下のような理由が考えられます。

  • libstdc++環境固有のヘッダであり、これを用いることはプログラムの可搬性を大きく損なうため
  • ヘッダのプリコンパイルによるコンパイル高速化を行わない場合、stdc++.hのインクルードは本来の使用目的から外れ、コンパイル時間を増大させる可能性があるため
  • コンパイル後のバイナリに不必要な未使用定数などが含まれ、サイズが増大するため
  • 不必要なヘッダをインクルードすることによってIntellisenseなどIDEの動作を重くする可能性があるため
  • ファイル中で標準ライブラリのどの機能を使用しているかがわかりにくくなり、プログラム全体の見通しが悪くなるため

最も致命的なのは、bits/stdc++.hを使うことによって得られる利点が「プログラムが短くなる」という点しか見当たらないことです。私は一般に、「プログラムが短くなる」とか「きれいに見えるようになる」などの理由でプログラムの動作に対する本質的な変更を加えることはバッドプラクティスであると考えます (もちろん、例外もあります)。

実際の開発において時としてDRY原則を外れたコードを書くことが要請されるように、泥臭いコードがかえってプログラムの保全性を上げることはままあります。表面的な見た目にとらわれず、常により「良い」コードを書けるようになりたいものです。

最後に

いかがでしたか?気に入ったなら是非、記事の上にある「LGTMボタン」を押して下さいね!

それでは皆さん、良い競プロライフを!