問題
C++ quiz time! Without checking, what does this print (assume an LP64 / LLP64 system):
— Richard Smith (@zygoloid) March 18, 2019
short a = 1;
std::cout << sizeof(+a)["23456"] << std::endl;
こんなツイートが人気を博していた。
# 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と誤答した人の脳内回路
誤答したの私だ。アンケートを見ても一番多い。
Huh. Cute. I'm C++ guru and I knew the type of +a is int. I also knew a[b] and b[a] is equivalent. It's so obvious. Not worthy of my time and the answer is .... Whaaaaaat!? Why?
— Ryou Ezoe (@EzoeRyou) March 19, 2019
江添さんがノリノリで誤答を書き込んでいる。
sizeof(+a)["23456"]をどうパースしたかというと
+asizeof(+a)sizeof(+a)["23456"]
とパースしてしまったのだと思う。一般にsizeof演算子は必ず()とつけて使いましょう、と多くの初心者本にかかれており、そして実際多くのC++erが()をつけて使うもんだから、うっかりsizeof(+a)とパースしてしまいそうになるが、operator[]の優先順位がめっちゃ高いことを忘れないでほしい(ブーメラン)
- 暗黙変換でintになるよね~
- sizeof取ったら4でしょ?
-
a[b]はb[a]とも書けるから文字列の4番めの要素は6だ
と考えていく。
不正解だ
4と誤答した人の脳内回路
6とほぼ同じだが+aが暗黙変換でintになることを見落とすと4になる
- shortでしょ?
- sizeof取ったら2でしょ?
-
a[b]はb[a]とも書けるから文字列の4番めの要素は4だ
と考えていく。
不正解だ
3と誤答した人の脳内回路
ごめんなさい、さっぱりわからない。アンケートでももっとも票数が少ないものの、殆ど無いってわけでもないくらい投票されている。
short a = 1;
だからよくわからんけど文字列の1番めの要素を見に行ってしまったとか??
解説
+a
改めて見ていこう。
short a = 1;
なのでaの型はshort型だ。では+aの型は?
実はint型になる。
単項演算子+の演算の際にintegral promotionという暗黙変換が行われるためだ。
まあ実はint型であるかは重要ではない。
(+a)["23456"]
そうだ、ここで誤ってsizeof(+a)を考えてしまうと誤りだ。
operator[]はポインタ演算*(a + b)のsyntax sugarであるというのは割と有名な小話だと思う。
int arr[3] = { 1, 2, 3 };
arr[1]; // => 2
1[arr]; // => 2
*(arr + 1); // => 2
つまりこの場合
(+a)["23456"]と"23456"[+a]は等価だ。
aの値は
short a = 1;
より1なので演算結果はchar型の3になる。
sizeof(+a)["23456"]
ようやくsizeof演算子の出番だ。(+a)["23456"]はchar型なので、sizeofを取ると1になる。
sizeof((+a)["23456"])となっていれば引っかかる人は少なかっただろうが・・・。
余談
@Chironian 氏御用達の型名を表示させるマクロTYPENAMEを使いつつ、問題のコードをいじると理解度が増すかもしれない。
# 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;
}
4
6
6
3
char
1
教訓
-
sizeof演算子を使うときは()でくくりましょう -
operator[]の優先順位の高さを忘れない
感想
まんまとひっかかった、くやしい。