Twitterで話題のC++ quiz time!の解説を誰も書かないので書く

# 問題

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">C++ quiz time! Without checking, what does this print (assume an LP64 / LLP64 system):<br><br>short a = 1;<br>std::cout &lt;&lt; sizeof(+a)[&quot;23456&quot;] &lt;&lt; std::endl;</p>&mdash; Richard Smith (@zygoloid) <a href="https://twitter.com/zygoloid/status/1107740875671498752?ref_src=twsrc%5Etfw">March 18, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

こんなツイートが人気を博していた。

```cpp
#include <iostream>
#include "typename.h"
int main()
{
    short a = 1;
    std::cout << sizeof(+a)["23456"] << std::endl;
}
```

答えは四択で`1`,`3`,`4`,`6`のどれですか？というもの。

だれか解説書くだろと思ったが、みんなTwitter上で満足してしまったらしい(見落としてたらすまん)。解説するか。

# 正解

`1`になります。

・・・えええっ！？

(私は6だと騙された)

# `6`と誤答した人の脳内回路

誤答したの私だ。アンケートを見ても一番多い。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Huh. Cute. I&#39;m C++ guru and I knew the type of +a is int. I also knew a[b] and b[a] is equivalent. It&#39;s so obvious. Not worthy of my time and the answer is .... Whaaaaaat!? Why?</p>&mdash; Ryou Ezoe (@EzoeRyou) <a href="https://twitter.com/EzoeRyou/status/1107896189389692928?ref_src=twsrc%5Etfw">March 19, 2019</a></blockquote>

江添さんがノリノリで誤答を書き込んでいる。

`sizeof(+a)["23456"]`をどうパースしたかというと

1. `+a`
2. `sizeof(+a)`
3. `sizeof(+a)["23456"]`

とパースしてしまったのだと思う。一般に`sizeof`演算子は必ず`()`とつけて使いましょう、と多くの初心者本にかかれており、そして実際多くのC++erが`()`をつけて使うもんだから、うっかり`sizeof(+a)`とパースしてしまいそうになるが、`operator[]`の優先順位がめっちゃ高いことを忘れないでほしい(ブーメラン)

1. 暗黙変換でintになるよね～
2. sizeof取ったら4でしょ？
3. `a[b]`は`b[a]`とも書けるから文字列の4番めの要素は`6`だ

と考えていく。

不正解だ

# `4`と誤答した人の脳内回路

`6`とほぼ同じだが`+a`が暗黙変換でintになることを見落とすと`4`になる

1. shortでしょ？
2. sizeof取ったら2でしょ？
3. `a[b]`は`b[a]`とも書けるから文字列の4番めの要素は`4`だ

と考えていく。

不正解だ

# `3`と誤答した人の脳内回路

ごめんなさい、さっぱりわからない。アンケートでももっとも票数が少ないものの、殆ど無いってわけでもないくらい投票されている。

```cpp
short a = 1;
```

だからよくわからんけど文字列の1番めの要素を見に行ってしまったとか？？

# 解説

## `+a`

改めて見ていこう。

```cpp
short a = 1;
```

なので`a`の型は`short`型だ。では`+a`の型は？

実は`int`型になる。

単項演算子`+`の演算の際にintegral promotionという暗黙変換が行われるためだ。

まあ**実はint型であるかは重要ではない**。

## `(+a)["23456"]`

そうだ、ここで誤って`sizeof(+a)`を考えてしまうと誤りだ。

`operator[]`はポインタ演算`*(a + b)`のsyntax sugarであるというのは割と有名な小話だと思う。

```c
int arr[3] = { 1, 2, 3 };
arr[1]; // => 2
1[arr]; // => 2
*(arr + 1); // => 2
```

つまりこの場合

`(+a)["23456"]`と`"23456"[+a]`は等価だ。

`a`の値は

```cpp
short a = 1;
```

より`1`なので演算結果は`char`型の`3`になる。

## `sizeof(+a)["23456"]`

ようやく`sizeof`演算子の出番だ。`(+a)["23456"]`はchar型なので、`sizeof`を取ると`1`になる。

`sizeof((+a)["23456"])`となっていれば引っかかる人は少なかっただろうが・・・。

# 余談

@Chironian 氏御用達の型名を表示させるマクロ`TYPENAME`を使いつつ、問題のコードをいじると理解度が増すかもしれない。

```cpp
#include <iostream>
#include "typename.h"
int main()
{
    short a = 1;
    std::cout
        << sizeof(+a) << std::endl
        << "23456"[4] << std::endl
        << 4["23456"] << std::endl
        << (+a)["23456"] << std::endl
        << TYPENAME((+a)["23456"]) << std::endl
        << sizeof(+a)["23456"] << std::endl;
}
```

https://wandbox.org/permlink/TT51W7xs3ZEfKpeq

```plain:実行結果
4
6
6
3
char
1
```

# 教訓

- `sizeof`演算子を使うときは`()`でくくりましょう
- `operator[]`の優先順位の高さを忘れない

# 感想

まんまとひっかかった、くやしい。

# License

[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja)

<a href="https://commons.wikimedia.org/wiki/File:CC-BY_icon.svg#/media/File:CC-BY_icon.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/CC-BY_icon.svg/1200px-CC-BY_icon.svg.png" alt="CC-BY icon.svg" width="88px"></a>