テンプレートに対する friend
なぜか VC6 は、
template< class T > friend class Hoge;
こんな感じで、テンプレートに対する friend をすることが出来ない(ARM なら出来る)。
で、これを解決するためにいろいろ考えてみた。
例えば、あるクラス Hoge があって、テンプレートな Factory に対して friend をする場合は、
template< class T > class Factory; class Hoge{ private: Hoge(){} ~Hoge(){} friend Factory< Hoge >; }; template< class T > class Factory{ public: static T* create(){ return new T; } };
普通はこうすればいいんだけど、もし、自前のアロケータの functor によって Hoge を生成する Factory を作りたい場合は、
template< class T , class M > class Factory{ public: static T* create(){ void* p = M()( sizeof( T ) ); if( p != null ) new( p ) T(); return (T*)p; } };
class MyAllocator{ public: void* operator()( size_t size ){ return allocator.allocate( size ); } };
こんな感じのプログラムを組むことになる。
この場合、M に何が渡されるか分からないので、Hoge で Factory を friend 指定することが出来ない。
どうすればいいかとしばらく考えてみて、メンバ関数テンプレートを使用すればいいことに気付いた。
class FactoryProxy; class Hoge{ ... friend FactoryProxy; }; class FactoryProxy{ public: template< class T , class M > static T* create( T* , M* ){ void* p = M()( sizeof( T ) ); if( p != null ) new( p ) T(); return (T*)p; } }; template< class T , class M > class Factory{ public: static T* create(){ return FactoryProxy::create( (T*)0 , (M*)0 ); } };
こうすれば、FactoryProxy は Hoge クラスの中で FactoryProxy を friend 指定すれば、Factory クラスから Hoge が生成出来るようになる。
一応これで解決なのだけれども、FactoryProxy が外部からアクセス出来るのが良くない。
これは Factory クラスからしかアクセス出来ないようにしたいんだけれども、だからと言って friend を指定しようとしても、最初の問題にぶち当たる。
これは、次のようにすればいい。
template< class T , class M > class Factory; class FactoryProxy{ public: template< class T , class M > static T* create( T* , M* , Factory< T , M > ){ void* p = M()( sizeof( T ) ); if( p != null ) new( p ) T(); return (T*)p; } }; template< class T , class M > class Factory{ public: static T* create(){ return FactoryProxy::create( (T*)0 , (M*)0 , Factory() ); } private: Factory(){} ~Factory(){} friend FactoryProxy; };
少々強引だけど、Factory テンプレートの実体を private にして、それを渡している。
こうすることによって、外部には公開されているけれども、どう間違ってもこれを使用することは出来ないようになる。
もし、Factory にメンバを持っていて、それが大きすぎるというのであれば、
template< class T , class M > class Factory; template< class T , class M > class FactoryTag{ private: FactoryTag(){}; friend class Factory< T , M >; }; template< class T , class M > class Factory{ public: static T* create(){ return FactoryProxy::create( (T*)0 , (M*)0 , FactoryTag< T , M >() ); } };
いらないクラスが増えるのがちょっと嫌だけれども、これで同じ事が出来る。
というか、なんでたかがテンプレートを friend するためにこんな苦労せにゃならんのだ(;´Д`)