1

次のようなファクトリ クラスの実装に使用するのが好きなパターンがあります (この質問に対する私の回答から抜粋)。

class Factory
{
public:
    template<class DerivedType>
    DerivedType::CreatedType *createType()
    {
        DerivedType::CreatedType *r = (DerivedType::CreatedType) (*(m_creators[DerivedType::id]))();
        return r;
    }
protected:
    static std::map<int,void *(*)()> m_creators;
};

std::map<int,void *(*)()> Factory::m_creators = std::map<int,void*(*)()>();

template<class Derived, class CreatedType>
class CRTPFactory : public Factory
{
    typedef typename CreatedType CreatedType;
public:
    static bool register() 
    {
        Factory::m_creators.push_back(std::make_pair(Derived::id,Derived::create);
        return true;
    }

private:
    static bool m_temp;
};

template<class Derived>
bool CRTPFactory<Derived>::m_temp = CRTPFactory<Derived>::register();

class AFactory : public CRTPFactory<AFactory,A>
{
private:
    static A *create() 
    {
        //do all initialization stuff here
        return new A;
    }

public:
    static const int id = 0;
};

これにより、ファクトリ クラスを変更することなく、新しい型のファクトリを拡張できます。また、ファクトリ クラスを変更せずに、さまざまな型の特定の作成アルゴリズムを実装することもできます。ただし、このパターンには大きな問題があります。クラス AFactory が明示的に使用されることはありません。CRTPFactory のメンバー temp を使用して、ロード時に作成者関数を登録します。これを理解するのは少し複雑かもしれませんが、使い方はとても簡単です。問題は、AFactory がコンパイルされていないため、読み込み時に静的パラメーターが初期化されないことです。私の質問は、明示的にインスタンスを作成せずにコンパイラ (私は VS 2012 を使用していますが、GCC の回答も良い) を強制的にコンパイルすることは可能ですか? 私が VS で使用する解決策は、AFactory を dllexport することです。そうすれば、コンパイラはクラスをインスタンス化していることを知らなくても、クラスをコンパイルします。これは、他の dll がそれをインスタンス化する可能性があると想定しているためです。このソリューションの問題点は、ファクトリ クラスを残りのコードとは別の dll に実装する必要があることです。また、これは GCC では機能しません。

4

3 に答える 3

3

from を継承するCRTPFactory<AFactory,A>と、クラスの暗黙的なインスタンス化が発生しますが、そのメンバーの定義は発生しません。

[temp.inst]

クラス テンプレートの特殊化の暗黙的なインスタンス化により、クラス メンバー関数、メンバー クラス、静的データ メンバー、およびメンバー テンプレートの宣言の暗黙的なインスタンス化が行われますが、定義または既定の引数のインスタンス化は行われません。

から継承する代わりに、メンバーCRTPFactory<AFactory,A>を明示的にインスタンス化するだけです。m_temp

template bool CRTPFactory<AFactory,A>::m_temp;

参考までに、修正された例を次に示します (コンパイル可能な形式で)。

#include <map>

class Factory
{
public:
    template<class DerivedType, class CreatedType>
    CreatedType *createType()
    {
        CreatedType *r = (CreatedType) (*(m_creators[DerivedType::id]))();
        return r;
    }
protected:
    static std::map<int,void *(*)()> m_creators;
};

std::map<int,void *(*)()> Factory::m_creators = std::map<int,void*(*)()>();

template<class Derived, class CreatedType>
class CRTPFactory : public Factory
{

public:
    static bool register_() 
    {
        Factory::m_creators.insert(std::make_pair(Derived::id,Derived::create));
        return true;
    }

  static bool m_temp;
};

template<class Derived, class CreatedType>
bool CRTPFactory<Derived, CreatedType>::m_temp = CRTPFactory<Derived, CreatedType>::register_();

struct A
{
};

class AFactory
{
public:
    static void *create() 
    {
        //do all initialization stuff here
        return new A;
    }

public:
    static const int id = 0;
};

template bool CRTPFactory<AFactory,A>::m_temp;
于 2013-07-04T10:00:30.490 に答える
1

あなたの仮定は間違っています。class AFactory間違いなくコンパイルされます。ヘッダーにあるため、おそらくかなりの回数です。

あなたの本当の問題はおそらくそれが登録されclass AFactoryていないことです。なぜでしょうか?どのステートメントがそれを引き起こすでしょうか? すべてのステートメントは、最終的にグローバル変数の初期化子から、またはグローバル変数の初期化子から呼び出されます。main()

于 2013-07-04T09:40:13.933 に答える
1

静的クラス メンバーは明示的にどこかに作成する必要があります。

選択したcppファイルで次のようなことを行うとうまくいくはずです:

int AFactory::id = 0
于 2013-07-04T09:23:03.833 に答える