C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++におけるautoとdecltypeの違いと関係性をわかりやすく解説

autoとdecltypeは役割が異なるため、1対1で対応する関係にはありません。autoはC++11以降で利用できるキーワードで、変数の型を自動的に推論するために使われます。一方、decltypeは型指定子であり、引数として渡された式の型をそのまま返します。autoが変数に代入される値をもとに型を推論するのに対し、decltypeは式自体から型を導き出す点が大きな特徴です。また、decltypeが返す型は、そのまま別の変数の宣言に利用することもできます。

autoの型推論ルール

autoはテンプレート引数推論(テンプレートパラメータの推論)と同じ規則に従って型を決定します。詳細については、cppreferenceの「テンプレート引数の推論」のページを参照してください。

decltypeの型推論ルール

decltypeには、C++の標準規格で定められた推論ルールがあります。以下はその抜粋です。

sizeof演算子と同様に、decltypeのオペランドは評価されません(未評価オペランド)。decltype(e)が返す型は、おおむね次のように推論されます。

  • 式eが、ローカルスコープや名前空間スコープの変数、静的メンバ変数、または関数の仮引数を参照している場合、結果はその変数・引数の宣言された型になる。
  • eが関数呼び出し、あるいはオーバーロードされた演算子の呼び出しである場合、decltype(e)はその関数の宣言された戻り値の型を表す。
  • それ以外の場合、eが左辺値(lvalue)であればdecltype(e)はT&(Tはeの型)、eが右辺値(rvalue)であれば結果はTとなる。

なぜこのような設計なのか

これらのセマンティクスは、ジェネリックライブラリの作者のニーズを満たしつつ、初心者プログラマにも直感的に理解できるように設計されています。decltypeが返す型は、常にソースコード上で宣言されたオブジェクトや関数の型と正確に一致するためです。より厳密に述べると、最初のルールは括弧で囲まれていないid式やクラスメンバアクセス式に適用され、関数呼び出しの場合は、オーバーロード解決の規則によって静的に選択された関数の戻り値の型が推論結果となります。

autoとdecltypeを使ったコード例

以下は、autoとdecltypeをそれぞれ使ってイテレータの型を推論する例です。

#include <iostream>
#include <vector>

using namespace std;

int main() {
    // autoによる型推論
    vector<int> arr(10);
    for (auto it = arr.begin(); it != arr.end(); it++) {
        cin >> *it;
    }

    // decltypeによる型推論
    vector<int> arr2(10);
    for (decltype(arr.begin()) it = arr2.begin(); it != arr2.end(); it++) {
        cin >> *it;
    }

    return 0;
}

この例では、autoではarr.begin()の戻り値から型を推論し、decltypeでは同じ式を型指定子として直接利用しています。どちらもvector<int>::iterator型として解決されます。

autoとdecltypeの微妙な違いに注意

decltypeが表す型は、autoが推論する型と必ずしも一致するとは限らない点に注意が必要です。たとえばautoは参照やconst修飾を剥がして推論することがありますが、decltypeは式の型を忠実に反映します。こうした微妙な差異について詳しく知りたい方は、Thomas Becker氏によるC++の型推論に関する詳細な解説(auto and decltype)を参照するとよいでしょう。

  1. C++におけるドット演算子(.)とアロー演算子(->)の違いを徹底解説

    C++では、ドット演算子(.)とアロー演算子(->)はどちらもクラスのメンバーにアクセスするために使用されますが、それぞれ使用される場面が異なります。C++において、class、struct、unionとして宣言された型はすべて「クラスタイプ」とみなされるため、以下の説明はこれら3つすべてに当てはまります。基本的な使い分けa.b は、b がオブジェクト a(またはオブジェクトへの参照[1])のメンバーである場合にのみ使用できます。つまり、a.b の形式では、a は常にクラスの実際のオブジェクト(またはその参照)でなければなりません。a->b は、本質的に (*a).b の省略記法で

  2. C++のautoキーワードとは?型推論の仕組みと使い方を徹底解説

    C++におけるautoキーワードは、C言語から「引き継がれた」歴史あるキーワードでした。しかし、長い間ほとんど使われることはありませんでした。状況が大きく変わったのは、C++11でautoが「文脈から型を推論する」機能を持つようになってからです。C++11以前では、すべてのデータ型をコンパイル時に明示的に宣言する必要がありました。そのため、プログラマーは式の型を常に意識してコードを書く必要があり、特に複雑なイテレータ型などを扱う際には記述が冗長になりがちでした。しかし新しいバージョンのC++では、型推論をコンパイラ自身に任せられるキーワードが導入され、プログラマーの負担が大幅に軽減されました。