昨日のエントリではかなりダメダメな感じになったのですが、「じゃぁBoost Libraryではどうやっているのか?」が知りたくなり、昨日のエントリの最後にBoost Libraryで書いたコードをプリプロセッサに通した結果を表示させてみた。
なんと、こんな感じだった!
// (長過ぎるので省略)
引数なしの関数から引数を9つとる関数までの定義がズラっと。それも普通の関数の場合、メンバ関数の場合、メンバ関数でconst
が指定されている場合、etc...。実際にはマクロを使って展開しているのだと思いますが、ここまでやっているとは思いませんでした。
安易に立ち向かおうとした自分が浅はかだった…。
それはそれとして。プリプロセッサを通して展開されたコードを見ていて気になったことが一つ。戻り値がある場合とない場合の区別がされていない。区別しなくていいんだろうか?
つまりこういうことです。
次のようなクラステンプレートがあった場合。
// 型がARG1の引数一つ、戻り値の型がRESULTの関数のポインタを保存するクラス template<typename RESULT, typename ARG1> struct Function { Function(RESULT(*f)(ARG1)) : f_(f) {} RESULT operator () (ARG1 arg1) { // 関数の呼び出し return f_(arg1); } RESULT (*f_)(ARG1); };
このようなクラステンプレートがあった場合、戻り値がない(戻り値の型がvoid
の)場合、関数の呼び出し方を変える必要があるのでは?次のように特殊化してやる必要があるのではないか?ということです。
// 型がARG1の引数一つ、戻り値の型がvoidの関数のポインタを保存するクラス template<typename ARG1> struct Function<void, ARG1> { Function(void(*f)(ARG1)) : f_(f) {} void operator () (ARG1 arg1) { // 関数の呼び出し f_(arg1); // returnしない } void (*f_)(ARG1); };
不審に思って検索してみたら、ありました。JIS X3014*1の79ページに、戻り値がvoid
の関数でreturn
文を書く場合、値がviod
となる式を書くことができる、とあります。
つまり、次のようなコードがOKなわけです。
void foo() { // do something return (void)(0); }
return
文に書く式は関数でもいいので、次のようなコードもOKになります。
void bar() { // do something } void baz() { return bar(); // 戻り値がvoidの関数をreturnする }
結果として。上で書いたような、戻り値がvoid
の場合に特殊化する必要がなくなります。
この辺りの仕様がC言語でどうなっていたかはわからないのですが、このvoid
を返すのOKというこの仕様、こういう場合のための仕様のような気がします。
なんとなくバッドノウハウの香り。