型はコンパイラに選ばせる
前のページでは、すべての C++ 変数が固定された型 — int、double、std::string など — を持つことを見ました。その型を手で書き出すのは int count = 0; なら問題ありませんが、型が長くなると煩雑になります。auto キーワード(C++11 以降)は、代入する値からコンパイラに型を 推論 させるので、値を一度書けばあとはコンパイラが埋めてくれます。
重要な考え方: auto は動的型付けでは ありません。各変数は依然として、コンパイル時に確定する 1 つの具体的な型を持ちます。auto はそれを書き出す手間を省くだけです。
auto が価値を発揮する理由
短くて自明な型なら、auto count = 0; と int count = 0; は同じくらい読みやすいです。auto が本当に効いてくるのは、長くて反復的な型名 — 標準ライブラリのコンテナやイテレータから生じるような型 — に対してです。
冗長な書き方と auto を使った書き方を比べてみましょう。
// auto なし - 実質的に型を 2 回書いている
std::vector<std::pair<std::string, int>>::iterator it = scores.begin();
// auto あり - コンパイラはすでに型を知っている
auto it = scores.begin();
どちらもまったく同じイテレータ型を宣言します。2 つ目のほうが読みやすく、後で scores を別のコンテナに変えても食い違いが起きません。
完全なプログラムの中で見てみましょう。
範囲ベース for ループでの auto
auto に最もよく出会う場所は、範囲ベースの for ループです。要素の型を手で書き出したいことはほとんどなく、auto をどう書くかでコピーになるか参照になるかが決まります。
目にする 3 つのパターンと、それぞれの意味:
for (auto x : v)-xは各要素の コピー です。intには安価ですが、大きなオブジェクトには無駄が多いです。for (auto& x : v)-xは 参照 です。要素をその場で変更できます。for (const auto& x : v)-xは読み取り専用の参照です。読むだけのときに使います。
次のプログラムは auto& を通してコンテナを変更します。
落とし穴: そのループで for (auto n : nums)(& なし)と書くと、n *= 10 はコピーだけを黙って変更し、nums には手をつけません。コンパイラは警告してくれません。ループは何も役立つことをしないだけです。
auto が取り除くもの
素の auto は、値渡しの関数引数とまったく同じやり方で型を推論します。つまり、最上位の const、参照、volatile を取り除きます。これは、特に指定しない限り auto は常に新しい変更可能なコピーを返すということです。
const を保持したい、あるいはコピーを避けたいなら、修飾子を自分で付け足します。コツは、どんな型でも飾るのと同じように auto を飾ることです。
つまり auto は 基底 の型を推論し、&、const、* はその上に付け足すつまみです。auto が型そのもので、const auto& はそれへの読み取り専用の参照です。
よくある間違いと落とし穴
auto は入力の手間を省いてくれますが、型を理解する必要までは省きません。初心者がはまる罠がいくつかあります。
初期化は必須です。 auto は空の宣言からは何も推論できないので、これは紛れもないコンパイルエラーになります。
auto x; // error: declaration of 'auto x' has no initializer
auto y = 0; // OK
整数リテラルは int であって double ではありません。 auto half = 1 / 2; は int を推論して 0 を格納します。なぜなら 1 / 2 は、auto がそれを見る 前に 整数除算だからです。型は値に従います。
auto は参照を取り除く — ダングリングコピーの不意打ちに注意。 関数が参照を返すのに素の auto で受け取ると、コピーになります。これはホットなループでは本物のパフォーマンスバグになることがあります(反復ごとに大きなオブジェクトのディープコピー)。「見るだけで持ち帰らない」と言いたいときは const auto& を使いましょう。
重要なときに型を隠さない。 auto result = compute(); は、compute の戻り値の型が文脈から自明なら問題ありません。しかし、読み手が result の正体を探し回らなければならないなら、型を書き出すほうが親切な選択になることがあります。auto はノイズを減らすためのものであって、意図を隠すためのものではありません。
次へ: 定数と const
ここまでで、auto は保持を頼まない限り意図的に const を取り除くことを見てきました。すると当然の疑問が湧きます。const は実際に何を保証するのか、そしてそもそもどんなときに値を変更不可と印付けすべきなのか? 次のページでは const、定数式、そして「とりあえずデフォルトで const にする」が C++ で最も役立つ習慣の 1 つである理由を掘り下げます。
よくある質問
C++ で auto キーワードは何をしますか?
auto はコンパイラに、変数の型をその初期化子から推論するよう指示します。auto x = 5; は x を int にし、auto y = 3.14; は y を double にします。型はコンパイル時に固定されます。auto は動的型付けではなく、自分で型を書く代わりの省略形です。
C++ で auto は const と参照を保持しますか?
いいえ。素の auto は最上位の const、参照、volatile を取り除きます。元が const int& r の場合、auto x = r; は素の int のコピーになります。これらを保持したいなら明示的に書きます。コピーせずに読み取り専用の参照を束縛するには const auto& を使います。
auto で変数を初期化せずに宣言できますか?
いいえ。auto x; はコンパイルエラーになります。コンパイラが型を推論する元となる初期化子がないからです。すべての auto 変数は、宣言した時点で値を与えなければなりません。