よく考えれば当たり前
例えば
struct hoge { void fuga( void ) {} }; reinterpret_cast< hoge * >( NULL )->fuga();
は通る+動く。まぁアーキテクチャ依存だろうけど。
つまりhoge::fuga()はコンパイル時に
static void fuga( hoge *this ) {} fuga( reinterpret_cast< hoge * >( NULL ) );
ってなる。で、これだけを頭に入れて書いたコード
struct base { virtual void fuga( void ) {} }; struct derived { void fuga( void ) {} }; reinterpret_cast< derived * >( NULL )->fuga();
はセグフォになる。最初何をいわれてるか解らなかったけど、これって仮想関数テーブルを参照したときに死ぬなと。中途半端な知識は人を殺すな。
で、こんなことかこうと思ったのがソケットのラッパを書いてて、TCPとUDPをそれぞれ作ってたとき
struct L4 { ... virtual __socket_type protocol_type( void ) const = 0; ... }; struct TCP : public L4 { ... __socket_type protocol_type( void ) const { return SOCK_STREAM; } ... }; struct UDP : public L4 { ... __socket_type protocol_type( void ) const { return SOCK_DGRAM; } ... };
ってやってたとき
L4 *ptr; ... if ( ptr->protocol_type() == reinterpret_cast< TCP * >( NULL )->protocol_type() ) { ... } ...
で、乙。
static __socket_type protocol_type( void ) { ... }
って定義も混ぜるとオーバーロードが定義できないって怒られるし、このためだけにわざわざインスタンスを一つ作るのも馬鹿らしい。virtualな関数が[[final]]だったときに、それをstaticでも定義し直せるような仕様だったらいいんだがな・・・
結局
struct TCP; struct UDP; template < typename > __socket_type GetProtocolType( void ); template <> __socket_type GetProtocolType< TCP >( void ) { return SOCK_STREAM; } template <> __socket_type GetProtocolType< UDP >( void ) { return SOCK_DGRAM; } struct TCP : public L4 { ... __socket_type protocol_type( void ) { return GetProtocolType< TCP >(); } ... }; struct UDP : public L4 { ... __socket_type protocol_type( void ) { return GetProtocolType< UDP >(); } ... }; L4 *ptr; ... if ( ptr->protocol_type() == GetProtocolType< TCP >() ) { ... } ...
で落ち着いた。んーでもやっぱ同じ関数名の方が美しいよなぁ・・・