Flat Leon Works

アプリやゲームを作ってます。

【C++】C++11以降の戻り値型の記述方法まとめ

C++11になり、後置き戻り値型、auto型、ラムダ式が導入され、戻り値の記述方法が大幅に増えました。またC++14でさらに新しい記述方法が加わったのでとてもややこしいことになっています。そこで戻り値型の記述方法をまとめてみることにしました。(勉強中のため間違っている箇所があるかもしれません)

戻り値型の形式( 前置き、後置き、省略 )

前置き形式

int Hoge( void ){ return 0; }
int [](){ return 0; } // コンパイルエラー

おなじみの戻り値の記述方法です。ラムダ式では前置きで記述することはできません。

後置き形式

auto Hoge( void ) -> int { return 0; }
[]() -> int { return 0; }

C++11から導入された記述方法です。通常関数で後置きを利用するには、前置き部分にプレースホルダーとしてautoと記述する必要があります。

省略形式

Hoge( void ){ return 0; } // コンパイルエラー
[](){ return 0; }

戻り値型を記述しない記述方法です。通常関数では利用できず、ラムダ式にのみ許されています。

戻り値型の記述形式まとめ

  • 通常関数は前置き戻り値が必須(後置きする場合もプレースホルダーとしてautoが必要)
  • 逆に、ラムダ式は前置き戻り値は禁止
  • 通常関数は戻り値型を省略できないが、ラムダ式は省略できる

戻り値型での型推論(auto)

戻り値型を明示せず、関数本体から推論させることができます。やり方がいくつかあります。

戻り値型での型推論の記述方法

  • 通常関数
    • 前置き戻り値型にautoを指定する(C++14から)
    • 後置き戻り値型にautoを指定する(C++14から)
  • ラムダ式
    • 後置き戻り値型にautoを指定する(C++14から)
    • 戻り値型を省略する

型推論の挙動

  • 関数本体にreturn文がなければ戻り値型はvoidになります
  • 関数本体にreturn文が複数ある場合は、共通の型が戻り値型になります

プレースホルダーとしての前置きauto

通常関数で後置き戻り値型記法のために前置きするautoはただのプレースホルダーであることに注意が必要です。

auto Hoge( void ) { return 0; } // 前置きautoは戻り値型の型推論を意味する
auto Hoge( void ) -> int { return 0; } // 前置きautoは後置き記法のためのプレースホルダーを意味する
auto Hoge( void ) -> auto { return 0; } // 前置きautoは後置き記法のためのプレースホルダーを意味する。後置きautoは戻り値型の型推論を意味する

参照として型推論させる(auto&)

autoのかわりにauto&と記述することで、型推論させた上でその型の参照型とすることができます。

戻り値型としてのdecltype(auto)

decltype(auto)の前にdecltype(式)の説明を行います。decltype(式)は型を表し、その型はから決定されます。ところでautoも型を表し、その型は式から決定されます。

decltype(10+1) x = 10+1; // decltype(10+1)は式10+1から int型 となる
auto y = 10+1; // autoは式10+1から int型 となる

同じように見えますが、式が参照型の場合に違いが出てきます。

int v = 0;
int& r = v;
decltype(r) x = r; // decltype(r)は式rから int&型 となる
auto y = r; // autoは式rから int型 となる

これがdecltype(式)autoの違いです。上記の例では変数定義でしたが、関数の戻り値型としてのdecltype(式)autoでも同じです。

そして、decltype(auto)は、decltype(式)の式部分の記述をコンパイラに推論させる記述方法です。本質的にはdecltype(式)なので、上記のように式が参照型の場合にはdecltype(auto)も参照型になります。

戻り値型としてdecltype(auto)を使った場合、decltype(return文の式部分)となります。

まとめ

通常関数 ラムダ式
前置き戻り値型 必須 不可
後置き戻り値型 可能 可能
戻り値型の省略 不可 可能
前置きauto 可能(C++11では後置き戻り値型のプレースホルダとしてのみ利用可能) 不可(そもそも前置き不可)
後置きauto 可能(C++14から) 可能(C++14から)
  • 前置きauto : 後置き戻り値型のプレースホルダー or 戻り値型の型推論(C++14から)
  • 後置きauto : 戻り値型の型推論(C++14から)
  • auto& : 型推論しつつ、参照型にする
  • autoとdecltype(式)の違い : decltype(式)は式が参照型の変数の場合、戻り値型も参照型になる
  • decltype(auto) : decltype(式)の式記述を省略した記法

(おまけ)戻り値型の記述パターンまとめ

「前置き」「後置き」「auto」「なし」の組み合わせを羅列してみました。

  • 前置きのみ
  • 後置きのみ
  • 前置きauto+後置き
  • なし(戻り値型の省略)
  • 前置きautoのみ
  • 後置きautoのみ
  • 前置きauto+後置きauto
//-----------------
// 前置き戻り値型
//-----------------
int Hoge( void ){ return 0; } // 通常関数
/* コンパイルエラー
int [](){ return 0; }; // ラムダ式
*/

//-----------------
// 後置き戻り値型(前置きautoなし)
//-----------------
/* コンパイルエラー
Hoge2( void ) -> int { return 0; } // 通常関数
*/
[]() -> int { return 0; }; // ラムダ式

//-----------------
// 後置き戻り値型(前置きautoあり)
//-----------------
auto Hoge3( void ) -> int { return 0; } // 通常関数
/* コンパイルエラー
auto []() -> int { return 0; }; // ラムダ式
*/

//-----------------
// 前後戻り値型の省略
//-----------------
/* コンパイルエラー
Hoge4( void ){ return 0; } // 通常関数
*/
[](){ return 0; }; // ラムダ式

//-----------------
// 前置きauto
//-----------------
auto Hoge5( void ) { return 0; } // 通常関数(C++14から可能)
/* コンパイルエラー
auto [](){ return 0; }; // ラムダ式
*/

//-----------------
// 後置きauto(前置きautoなし)
//-----------------
/* コンパイルエラー
Hoge6( void ) -> auto { return 0; } // 通常関数
*/
[]() -> auto { return 0; }; // ラムダ式(C++14から可能)

//-----------------
// 後置きauto(前置きautoあり)
//-----------------
auto Hoge7( void ) -> auto { return 0; } // 通常関数(C++14から可能)
/* コンパイルエラー
auto []() -> auto { return 0; }; // ラムダ式
*/

参考リンク