2005-06-28-01-半徹の後日談
>> Site top >> weblog >> 月別アーカイブ >> 2005年06月のlog >> 2005-06-28-01-半徹の後日談
最終更新日付:2005/06/28 01:00:00
半徹の後日談
2005 年 06 月 28 日
原因不明の理由で通るべきコードがコンパイルされない...というトラブルでまたもや半徹してしまった。が、どうやら大きな勘違いをしてしまっていたようだ。
最初、以下のように実装した。
class Record { // ... public: void SetValue( const void* p, UInt16 len ); void GetValue( void* p, UInt16 len ); }; template<typename T> inline Record& operator=( Record& lhs, const T& rhs ) { lhs.SetValue( &rhs, sizeof( T ) ); return lhs; } template<typename T> inline T& operator=( T& lhs, const Record& rhs ) { rhs.GetValue( &lhs, sizeof( T ) ); return rhs; }
...コンパイル OK。なんの疑問も感じず、文字列ポインタを正常に処理するために以下の明示的特殊化を追加した。
template<> Record& operator=<char*>( Record& lhs, const char*& rhs ) { // ... }
ところが、これがコンパイルエラーになってしまった。「不当な operator= です」とのこと...おかしいな。演算子オーバーロードのテンプレートは特殊化できないのか...? そこまで対応していないコンパイラはいくつかあるから、CodeWarrior もそうなのかもしれない...思いつく限りの可能性を試してみたが、全てはねつけられてしまった。で、代入演算子テンプレートを削除して普通の代入演算子オーバーロードを書いてもコンパイルエラーになってしまったとき、これがテンプレートの問題ではないことに気づいたのだ。
なんと、大域関数として代入演算子を定義できない! なぜかテンプレートとして定義するとできる。なんじゃそりゃ...こんなん Visual C++ 6.0 でもできるぞ...ほれ、こうやって、こう...あれ? コンパイルエラー。おかしいな。
で、今日になって職場においてある D & E ( The Design & Evolution of C++ ) を確認して納得。Release 2.0 以降の C++ では、代入演算子のオーバーロードはクラスメンバとしてしか定義できなくなっているのだ。自分の知識が古かったというよりは、自分の思い違いである。なぜなら自分が C++ を使い始めたのはもう ISO 標準が承認されるかされないかという時期だったからだ。
ということは...である。Record クラスが知らない型のオブジェクトの入出力を代入演算子でオーバーロードできないということではないか! これでは連想配列イメージの実装が頓挫してしまう。仕方がないので、連想配列イメージで Database オブジェクトが返した Record オブジェクトに対してストリームのイメージでデータを入出力するようにした。
Database db( "myDB" ); db["name"] << "kagelow"; db["age"] << 30;
これなら、未知の型に対しても入出力をオーバーロードできる。
struct UserData { public: UserData( CString n, UInt16 a ); public: CString name; UInt16 age; }; Record& operator << ( Record& lhs, const UserData& user ) { lhs << user.name << user.age; } UserData me( "kagelow", 30 ); db["key"] << me;
この方法に関する問題は、operator<<( ) の呼出しの度にレコードの伸張が発生することだが、よほど低水準(で使いにくい)インタフェイスにしない限り、この問題は解消しそうにない。仕方がないのでこれで行くことにする。
コメント
このページのタグ
Page tag : Palm
Copyright(C) 2005-2021 project-enigma.
Generated by CL-PREFAB.