2

静的データ メンバーと関数のみでテンプレート化されたクラスを構築したいと考えています。基本的には、コードのさまざまな部分に入力したい内部データを含む関数のコレクションです。に到着する前に、コンテンツをデータ メンバーに挿入しようとしていますmain()。これは、テンプレート化されていないクラスではうまく機能しますが、テンプレート化されたクラスでは、それを機能させる方法がわかりません。

コードは次のとおりです。

#include <iostream>
#include <vector>

class Foo{
private:
    static std::vector<unsigned> content;
public:
    static void insert(unsigned u){ content.push_back(u); }
    static size_t size(){ return content.size(); }
};
std::vector<unsigned> Foo::content=std::vector<unsigned>();

struct Bar{ 
    Bar(){ Foo::insert(0);  }
} bar; 
// this works fine in gcc, but is this consistent or am I lucky?
// Foo::content will contain 0 prior to entering main

template <typename T>
class Quux{
private:
    static std::vector<T> content;
public:
    static void insert(T t){ content.push_back(t); }
    static size_t size(){ return content.size(); }
};
template <typename T>
std::vector<T> Quux<T>::content=std::vector<T>();

struct Wobble{ 
    Wobble(){ Quux<unsigned>::insert(0);    }
} wobble;  
// this does not work
// Quux<unsigned>::content will be empty prior to entering main

int main(){

    std::cout << Foo::size() << std::endl;  
    // outputs 1, as desired

    std::cout << Quux<unsigned>::size() << std::endl;   
    // outputs 0, makes me sad :(

    Wobble wobble2;
    std::cout << Quux<unsigned>::size() << std::endl;   
    // outputs 1, as desired
}

出力:

1
0
1

Fooはテンプレート化されていないクラスであり、構造体を介してFoo::content実行する前に問題なく挿入できます。私が幸運であるのではなく、これが一貫した行動であることを願っていますか?main()Bar

ただし、テンプレート化されたクラスに対して同じことをしようとすると、何かを追加できるようにQuux<T>なるまで待つ必要があるようです。main()なぜこれが必要なのか、そして(うまくいけば)それを回避する方法を誰かが説明できますか?テンプレートのインスタンス化の時間と関係があると思いますが、正確な理由はわかりません。Quux<unsigned>次の後に完全に利用できるようになると思います。

struct Wobble{ 
    Wobble(){ Quux<unsigned>::insert(0); }
} wobble;  

ここで何が欠けていますか?mainviaの前にテンプレート化されていないクラスにコンテンツを追加できるのに、なぜbar同じことができないのwobbleですか? テンプレート化されたクラスFooと同じ動作を得る方法はありますか?Bar

4

3 に答える 3

3

[basic.start.init]/2

明示的に特殊化されたクラス テンプレートの静的データ メンバーの定義には、初期化の順序があります。他のクラス テンプレートの静的データ メンバー (つまり、暗黙的または明示的にインスタンス化された特殊化) には、順序付けされていない初期化があります。[...] 単一の翻訳単位内で定義された順序付けられた初期化を持つ変数は、翻訳単位での定義の順序で初期化されます。[...] それ以外の場合、変数の順序付けされていない初期化は、他のすべての動的初期化に対して不定に順序付けられます。

私が理解している限り、 の初期化は の初期化Quux<unsigned>::contentと不定に順序付けられるため、未定義の動作がありwobbleます。

[導入]/13

評価 A と B は、A が B の前にシーケンスされるか、B が A の前にシーケンスされるかのいずれかの場合、不定にシーケンスされますが、どちらが指定されていません。

つまり、プログラムが動的に初期化されていない にアクセスする可能性がありますQuux<unsigned>::content

明示的な特殊化が問題を解決します。


動的初期化の前に、ゼロ初期化が行われることに注意してください。したがって、ポインタと動的メモリ割り当てを使用して、初期化順序の問題を克服することができます。

template <typename T>
class Quux{
private:
    static std::vector<T>* content;
    static void create() { if(!content) content = new std::vector<T>; };
public:
    static void insert(T t){ create(); content->push_back(t); }
    static size_t size(){ create(); return content->size(); }
};
template <typename T>
std::vector<T>* Quux<T>::content;

これにより、プログラムの最後に「メモリ リーク」が発生します。それが問題である場合は、deleter オブジェクト、つまりcontent、独自の破壊 (RAII の半分) で削除する別の静的データ メンバーを追加できます。

于 2013-07-03T08:50:14.373 に答える
1

Wobble で使用する前に、明示的なテンプレートのインスタンス化を追加できtemplate class Quux<unsigned>;
ます http://ideone.com/clone/mkJo0W

于 2013-07-03T08:48:18.117 に答える
1

最初のウォブルを定義する前に、符号なしのコンテンツ ベクトルの明示的なインスタンス化を追加する必要があります。

template<>
std::vector<unsigned> Quux<unsigned>::content=std::vector<unsigned>();

その後、意図したとおりに動作し、1、1、2 を出力します。

于 2013-07-03T08:49:38.560 に答える