読者です 読者をやめる 読者になる 読者になる

【初心者C++er Advent Calendar 2016 3日目】C++ で型推論(auto)しよう

初心者C++er Advent Calendar 2016 3日目の記事です。 3日目が空いてるようなので書いてみました。 今回は初心者向けの Advent Calendar ということで軽めの記事になります。
また、本記事では C++14 を基準としたコードになります。

[C++ で型を書く場面]

さて、C++ は静的型付言語なので、いろんな場面で intfloatstd::string などと言った型を書くことが多いです。

// 関数の戻り値型や引数型
int
func(std::string, float);

// 変数の宣言時に型を指定する
int a = func(std::string("homu"), 3.14f);

今回はこれらの型を auto を使用して省略できる場面を紹介したいと思います。

[変数の宣言時に auto を使う]

変数の宣言時に auto を使用することで右辺値から型推論をしてコンパイラが自動的に型を割り当てます。

// 代入式の右辺値である 42 からコンパイラが型を推論して自動的に型を割り当てる
// 42 は int 型なので n は int 型になる
auto n = 42;

// f は float 型である
auto f = 3.14f;

std::vector<int> v;

// it は std::vector<int>::iterator 型になる
auto it = std::begin(v);

このように本来は

std::vector<int>::iterator it = std::begin(v);

というように型が長くなってしまう場合、auto を利用することで

auto it = std::begin(v);

という風に簡略化することが出来ます。
また、当然ですがこの場合は『変数の初期値』を指定する必要があります。

[戻り値型を auto にする]

関数の戻り値型を auto にすることで return 文などからコンパイラが自動的に戻り値型を推論します。

// この関数の戻り値型は int 型 + float 型の結果(float 型)になる
auto
plus(int a, float b){
    return a + b;
}

// 複数の return をする場合は『同一の型』を return する必要がある
auto
func(int flag){
    if( flag ){
        return std::string("OK");
    }
    else {
        // コンパイル時に型を決定する必要があるので違う型を return することは出来ない
        // return -1
        return std::string("NG");
    }
}

// 戻り値を省略した場合は void 型になる
auto
hello_world(){
    std::cout << "hello, world" << std::endl;
    // こっちでもおk
//     return;
}

このように戻り値型に auto を使用することが出来ます。
注意点としては必ず『同じ型の値を return する』ということですね。
また、残念ながら関数の引数型で auto を使用して型推論を行うことは出来ません。

[ラムダ式の引数で auto を使用する]

先ほど関数の引数では auto は使えないと書きましたが、実はラムダ式の引数であれば auto を使用する事が出来ます。

// 引数型を auto にすることでどんな型でも受け取ることができる
auto output = [](auto a) { std::cout << a << std::endl; };
output(42);
output("homu");
// std::vector<int> も渡すことはできるが、std::cout に対応してないのでエラー
// output(std::vector<int>{});


// 戻り値型の auto と組み合わせて使用することもできる
// この場合、戻り値型は引数型に依存する
auto plus = [](auto a, auto b) -> auto { return a + b; };

plus(1, 2);       // int + int = int
plus(1, 3.14f);   // int + float = float
plus(std::string("homu"), std::string("mami"));  // std::string + std::string = std::string

ラムダ式の場合、引数型によって戻り値型が変わるというのが面白いですね。
C++ では で定義されてる関数にラムダ式を渡すことが多いので、このように引数型を型推論できることでかなり使い勝手がよくなると思います。

[まとめ]

C++ では複雑な型を扱うことが多いのですが、このように auto を使用することで冗長なコードをすっきり記述できる場面が多いです。
また、今回は使用しませんでしたが template を使用することで更に型を省略することも出来ます。
ただ、C++ では『型について理解することも重要』なので初心者の方はまず最初に『どんな型として扱わるのか』と考えてみましょう。
この関数の戻り値型は何か、式を演算した結果何型を返すのか、暗黙の型変換とは etc...と C++ で型について考えることは尽きません。
その上で型に慣れてきたら auto を使ってみるとよいと思います。
そんな感じでざっくりとして内容ですが 初心者C++er Advent Calendar 2016 3日目の記事になります。

[参照]