meryngii.neta

今日も新たな"ネタ"を求めて。

複雑な宣言

ほとんどC言語ネタなわけだが、C++0xの9月分のドラフトまでもう少しだと思うので、それまでつなぎを書いておく。

#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>

typedef int* DtoI(double);
typedef DtoI* V10[10];
typedef V10* f(char);

typedef int* (*(*g(char))[10])(double);

BOOST_STATIC_ASSERT((boost::is_same<f, g>::value)); // OK

D&E 2.8.1に載っている複雑な宣言のシンタクスだ。fとgはどちらも、int型へのポインタを返してdouble型の引数を取る関数へのポインタが10個並んだ配列へのポインタを返してchar型の引数を取る関数なわけだけれども、gを書くのに随分時間を使ってしまった。
複雑な宣言を読むには、変数名から型名に行き着くトップダウン式の方がいい、と私は実際にやりながら思った。型名から変数名に行き着くボトムアップ式は、最後まで変数の正体が分からないのでイメージしにくい。
英語だとトップダウン式、日本語だとボトムアップ式の方が合っているようだ。まあ私はコードが書ければいいので、いちいち自然言語に直す必要はないと思う。
まず()がついたら関数、[ ]がついたら配列、*がついたらポインタで、先に来るやつの方が優先順位が高い。でも()と[ ]はどちらも右結合なので、*が低いとだけ覚えておけばいい。
()は結合を変えるためにも使うけれども、変数名を中に含んでいなければ関数の引数部分になる。

typedef int *func1();
typedef int *(func2());
typedef int (*func3)();

BOOST_STATIC_ASSERT((boost::is_same<func1, func2>::value)); // OK
BOOST_STATIC_ASSERT((boost::is_same<func1, func3>::value)); // Error

func2は関数で、その戻り値は整数を指すポインタ。func3はポインタで、それは整数を返す関数を指す。func1については()の方が優先順位が高いのでfunc1と()がくっついて、func2と同じ型になる。
そして、最初に出てきたのは、

typedef int* (*(*g(char))[10])(double);

これもgから読み進めて行けばいい。
ちなみにD&Eでは、初期の段階で次のように改善するつもりだったことが書かれている。

int g(char)->[10]->(double)->;