知らなかったC++の仕様

最近、C++ を少し真面目に勉強し始めました。
もう５年以上 C++ を書いていますが、自分がいかに無知だったかを思い知らされました。
ということで、自分が知って驚いた C++ の仕様をまとめます。
C++11 現在の仕様(たぶん)なので、C++14 とか C++17 では変わっている可能性もあります。


## 戻り値を返さなくても良い関数

戻り値がある関数は、必ず値を返さなければなりません。
しかし、１つだけ例外的に戻り値を返さなくても良い関数があります。
main 関数です。

main 関数の戻り値は int ですが、return 文を書かなくてもコンパイルは通ります。
return 文を省略した場合、```return 0;``` と見なされます。


## 配列の添え字

一般的に、配列の添え字は ```a[1]``` のように [] の中に配列要素のインデックスを指定します。
これで指定した要素にアクセスできます。

```c++
int a[3] = { 0, 1, 2 };
int b = a[1];
```

しかし、x[y] の x が配列のポインター、y がインデックスという規格はありません。
規格上は、x か y の、どちらか一方が配列のポインターで、もう一方がインデックスであれば良いのです。
つまり ```a[1]``` ではなく ```1[a]``` と書いてもコンパイルは通ります。

```1[a]``` という書き方は一般的じゃないし、直感的に意味が分かりにくいので、もし見かけたら文句を言うと思いますけど、規格上は問題ありません。


## char != signed char

```int == signed int``` です。
```short == signed short``` です。
```long == signed long``` です。
が、```char == signed char``` ではありません。
```char == unsigned char``` でもありません。
char が signed になるか、unsigned になるかは実装依存です(一般的には signed だと思いますが)。

signed で実装されているからと言っても、```char == signed char``` になるわけではありません。
```char```、```signed char```、```unsigned char``` はそれぞれ別物です。

以下はコンパイルエラーになります。
なぜなら ```short``` と ```signed short``` は同じ型なので「```toInt``` の再定義」になるからです。

```c++
int toInt( short s ) {
    return s;
}
int toInt( signed short s ) {
    return s;
}
```

以下はコンパイルが通ります。
```char``` も ```signed char``` も ```unsigned char``` も別の型なので再定義にはなりません。

```c++
int toInt( char c ) {
    return c;
}
int toInt( signed char c ) {
    return c;
}
int toInt( unsigned char c ) {
    return c;
}
```


## 整数プロモーション

```char``` と ```short``` は、演算時に自動的にプロモーションが行われます。

プロモーションとは、```int``` よりもサイズの小さい型から ```int``` への変換のことです(```bool```は除く)。
```int``` 以上の型からそれ以上に型への変換、サイズの大きい型から小さい型への変換は「型変換」と呼ぶらしいです。
なお、```unsigned char``` や ```unsigned short``` の場合は ```unsigned int``` に変換(プロモーション)されます。

以下の b の型は int になります。

```c++
short a = 0;
auto b = a + a;
```

また、

```c++
char a = 0;
printf("%lu\n", sizeof(a));
printf("%lu\n", sizeof(-a));
printf("%lu\n", sizeof(a + a));
```

の結果は

```
1
4
4
```

になります。


## 関数の実引数の評価順

関数の実引数に式を指定する場合、どの順番で評価されるかは実装依存です。
もちろん、関数が呼ばれる前に全ての式が評価されることは保証されています。

以下を実行した結果、
clang 4.0 では ```123123123``` でしたが
gcc 6.3 と msvc++ 2015 では ```321321321``` でした。
clang は実引数の先頭から、gcc と msvc++ は末尾から評価しているようです。

```cpp
std::string& func1( std::string& s ) {
    s += "1";
    return s;
}

std::string& func2( std::string& s ) {
    s += "2";
    return s;
}

std::string& func3( std::string& s ) {
    s += "3";
    return s;
}

void outputString( std::string& s1, std::string& s2, std::string& s3 ) {
    std::cout << s1 << s2 << s3 << std::endl;
}

int main(void){
    std::string s = "";
    outputString( func1( s ), func2( s ), func3( s ) );
}
```


## [おまけ] free や delete は NULL チェック不要

これは２年ぐらい前まで知りませんでした。
意外と知らない人が多そう。

delete や free は NULL を渡しても問題ありません。

よく

```cpp
if ( p ) {
    delete p;
}

// or

if ( p ) {
    free( p );
}
```

という実装を見かけますが、NULLチェックは不要です。
以下の p が nullptr や NULL でも何も起きません(クラッシュしません)。

```cpp
delete p;

// or

free( p );
```

私もそうでしたが、なんとなく危険な感じがして、盲目的に NULL チェックしている人が多いのではないでしょうか。


## あとがき

「知らなかった」は言い換えると「知らなくても困らなかった」とも言えます。
運良く(？)、それを知らないと困るような仕事をしてこなかったとも言えます。

配列の添字とかは知らなくても何も困らないと思いますが、char の話とか整数プロモーションの話は知っておいた方が良さそうですね。

意外と知らない人も多いと思うので、周りのC++プログラマーに話したらドヤれるかもしれませんw