これは、使用例を見れば一目瞭然でしょう:
#include <gintenlib/cast.hpp> #include <iostream> using namespace std; // 適当に多重定義関数を用意します void hoge( int ) { cout << "int version\n"; } void hoge( void* ){ cout << "void* version\n"; } // 同様にクラスに対しても関数を用意します // なんとなく noncopyable なクラスで作る #include <boost/noncopyable.hpp> class base : boost::noncopyable {}; struct derived : base {}; void fuga( base& ) { cout << "base& version\n"; } void fuga( derived& ){ cout << "derived& version\n"; } int main() { // 予め using 宣言すれば四文字で使えて幸せ using gintenlib::cast; // int 版 hoge( 0 ); // void* 版。static_cast<void*>(0) でもOKだが長い hoge( cast<void*>(0) ); derived x; // あるいは base x; // base& 版 fuga( cast<base&>(x) ); // derived& 版。base x; の場合はコンパイルエラー fuga( cast<derived&>(x) ); // static_cast だと、コンパイルエラーにならない }
結果:
int version void* version base& version derived& version
このように、単純に「型を変換する」ことができます。
static_cast
を用いてもよいのですが、ポインタや参照関連で安全性が損なわれる可能性があります。 gintenlib::cast
はこの点で完全に安全、文字数も予め using
宣言しておけば四文字と、手軽な型変換にぴったりの代物となっています。
・・と、偉そうに語りましたが、実はこれ、 Boost に implicit_cast
という同機能の代物があります(実装は僅かに違いますが機能は全く同じ)。
というわけで、移植性を考える場合には、迷わず Boost の方を使いましょう。
一応「文字数が少ない」という便利さが残っているので、削除はしませんが :)
さてここからは、ちょっとした舞台裏の小話。読んでも読まなくても。
さて、この gintenlib::cast
の実装ですが、最初、こんな感じになっていました:
template<typename Target, typename Source> inline Target cast( const Source& src ) { return src; }
まー普通のコードで、特に問題はない・・・と思いきや、このコードには弱点があります。
それは「参照型の変換ができない」という点。 const&
で受け取っているので当然ですね(実はもう一つ弱点がありますが、当時は気付きませんでした。今気付いた貴方は凄い)。
そこで、 boost::enable_if
を用いて、このように書き換えます。
// 参照版 template<typename Target, typename Source> inline Target cast( Source& src, typename boost::enable_if< boost::is_reference<Target> >::type* = 0 ) { return src; } // 非参照版 template<typename Target, typename Source> inline Target cast( const Source& src, typename boost::disable_if< boost::is_reference<Target> >::type* = 0 ) { return src; }
これで、参照に対しても、きちんと動くようになりました。
ただ、不満点としてソースコードが長くなってしまったので、 マクロ GINTENLIB_ENABLE_IF ってのを作って、
// 参照版 template<typename Target, typename Source> inline Target cast( Source& src, GINTENLIB_ENABLE_IF(( boost::is_reference<Target> )) ) { return src; } // 非参照版 template<typename Target, typename Source> inline Target cast( const Source& src, GINTENLIB_DISABLE_IF(( boost::is_reference<Target> )) ) { return src; }
としました。これで cast<Base&>( derived )
的なコードも動きます。
これで gintenlib::cast
は完成した・・・と思いきや、実はまだ欠点が。
それは cast<void*>(0);
というコードが動かない点です。 int
から void*
には変換できないぞ、と言われてしまいます。
まー当然です。前情報なしに 0
って書いたら、そりゃ整数リテラルだと判断されてしまうので。
これをどうしようものか、と考えていた僕の頭に、ある日突然アイデアが降ってきました。それは、そもそもニ引数テンプレートにしなくても、
template<typename Target> inline Target cast( Target src ) { return src; }
とすればいい、というもの。
とはいえ、これでは「対象の型を明示しなくても動いてしまう」という弱点が有ります。それに、値渡しになるため、余計なコピーが行われる可能性も。
僕はとりあえず前者は置いておき、後者に対応することにしました。こういうときは boost::call_traits
です。
template<typename Target> inline Target cast( typename boost::call_traits<Target>::param_type src ) { return src; }
こう書いて、おや、と思います。これで前者の問題も解決したじゃないですか。というわけで、これで完成です。
・・・とまぁ、銀天ライブラリは、こんな感じで回り道しながら、ゆっくり作られているのでした。
ちなみに後で boost::implicit_cast
の存在を知った時、ソースコード見たら似た感じになっていて、ちょっとだけ誇らしかったのですが、同時に自分が車輪を最発明していた事実に気付き、軽く凹んだり。
確かに存在しない道理は無いよなー。いや、うっかりうっかり