テンプレートに対する 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 するためにこんな苦労せにゃならんのだ(;´Д`)