問題
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"]
をどうパースしたかというと
+a
sizeof(+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[]
の優先順位の高さを忘れない
感想
まんまとひっかかった、くやしい。