C++では返り値の型がvoidでない関数からreturn文を使わずに戻ろうとしてはダメ、ゼッタイ。

# 概要

C++では、返り値の型が`void`でない関数から`return`文を使わずに戻ろうとしてはいけません。

# なぜいけないか

C++では、返り値の型が`void`でない関数の実行が`return`文を通らずに最後に到達すると、**未定義動作**になります。
未定義動作になると何が起こってもおかしくありません。

# C言語では？

C言語では、返り値の型が`void`でない関数から`return`文を使わずに戻ろうとしただけでは未定義動作にはなりません。
ただし、その戻り値を使うと**未定義動作**になります。

# 根拠は？

## C言語では

C言語の規格書に近いとされる[N1570](http://chimera.roma1.infn.it/SP/COMMON/iso-iec-9899-1990.pdf) 6.9.1 Function definitions の 12 に、以下の記述があります。

> If the } that terminates a function is reached, and the value of the function call is used by
the caller, the behavior is undefined.

関数最後の`}`に到達し(すなわち、`return`文をつかわずに戻り)、
かつ**その返り値が呼び出し手によって使われる**と未定義動作になる、とされています。

## C++では

C++の規格書に近いとされる[N3337](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) 6.6.3 The return statement の 2 に、以下の記述があります。

> Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

関数の最後まで実行する(すなわち、`return`文をつかわずに戻ろうとする)と、
値を返す関数(すなわち、返り値の型が`void`でない関数)では未定義動作になる、とされています。

# 実際のコンパイル例

[Compiler Explorer](https://gcc.godbolt.org/) を用いて以下のコードをコンパイルしてみます。
`hoge`は返り値の型が`void`でなく`return`文が無い関数(**危険**)、
`fuga`は返り値の型が`void`である関数、`foo`は返り値の型が`void`ではなく`return`文がある関数です。

```c
#include <stdio.h>

int hoge(int a) {
    printf("hoge %d\n", a);
}

void fuga(int a) {
    printf("fuga %d\n", a);
}

int foo(int a) {
    printf("foo %d\n", a);
    return 0;
}
```

## C言語の場合

関数`hoge`、`fuga`では別の関数を`jmp`で呼び出すことでその関数からの戻りを利用して戻り、
関数`foo`では`ret`命令を用いて戻っています。
これだけでは、危険は無いようです。

```
.LC0:
        .string "hoge %d\n"
hoge:
        movl    %edi, %esi
        xorl    %eax, %eax
        movl    $.LC0, %edi
        jmp     printf
.LC1:
        .string "fuga %d\n"
fuga:
        movl    %edi, %esi
        xorl    %eax, %eax
        movl    $.LC1, %edi
        jmp     printf
.LC2:
        .string "foo %d\n"
foo:
        subq    $8, %rsp
        movl    %edi, %esi
        xorl    %eax, %eax
        movl    $.LC2, %edi
        call    printf
        xorl    %eax, %eax
        addq    $8, %rsp
        ret
```

[https://gcc.godbolt.org/z/fWKToh](https://gcc.godbolt.org/z/fWKToh)

## C++の場合

関数`fuga`では別の関数を`jmp`で呼び出すことでその関数からの戻りを利用して戻り、
関数`foo`では`ret`命令を用いて戻っています。

一方、この例においては、**未定義動作**である関数`hoge`には関数から戻る命令が無く、
その次にある文字列を機械語命令として実行しに行ってしまいます。
その結果、デタラメな実行結果になったり、不正な命令があったとして強制終了したりする可能性があります。

```
.LC0:
        .string "hoge %d\n"
hoge(int):
        movl    %edi, %esi
        subq    $8, %rsp
        movl    $.LC0, %edi
        xorl    %eax, %eax
        call    printf
.LC1:
        .string "fuga %d\n"
fuga(int):
        movl    %edi, %esi
        xorl    %eax, %eax
        movl    $.LC1, %edi
        jmp     printf
.LC2:
        .string "foo %d\n"
foo(int):
        subq    $8, %rsp
        movl    %edi, %esi
        xorl    %eax, %eax
        movl    $.LC2, %edi
        call    printf
        xorl    %eax, %eax
        addq    $8, %rsp
        ret
```

[https://gcc.godbolt.org/z/hqcaoK](https://gcc.godbolt.org/z/hqcaoK)

# 結論

C言語では`return`文を使わずに戻るだけでは未定義動作にならないとはいえ、
せっかく値を返すように宣言されているのにその値を全く使わない、というのはおかしいです。
また、C++では`return`文を使わずに戻るだけで不正動作の原因になり得ます。
したがって、値を返さない関数の戻り値の型はC言語、C++ともに`void`にしましょう。
また、うっかり値を返す関数で`return`文を実行させるのを忘れても気付けるよう、
コンパイラを警告を出す設定にしましょう。