2017-11-23 13 views
2

ここでは、作成しようとしているプールされたオブジェクトの最小の実例を示します(明らかに機能は完全ではありません -static class templateメンバ:不完全な型の 'sizeof'の無効なアプリケーション

:それから operator newを用いたテンプレートパラメータ Tの静的クラスメンバーのうち割り当てる基本クラステンプレート PoolObjを有する

template<typename T, std::size_t N> 
struct Storage 
{ 
    std::aligned_storage_t<sizeof(T), alignof(T)> data[N]; 
}; 

:私はaligned_storageを含むクラステンプレートStorageを有する)を有する

mは

template<typename T> 
struct PoolObj 
{ 
    static void* operator new(std::size_t size) 
    { 
     std::cout << "new T\n"; 
     return &T::pool.data[0]; 
    } 

    static void operator delete(void* p, std::size_t size) 
    { 
     std::cout << "delete T\n"; 
    } 
}; 

今私はPoolObjから継承するクラスを持っている、と私はnewを使用してインスタンスを作成するとき、私はプールからストレージを取得するように、静的Storageメンバーpoolを持っています。

struct Foo : PoolObj<Foo> 
{ 
    static Storage<Foo, 10> pool; 
}; 

Storage<Foo, 10> Foo::pool {}; 

このすべてが正常に動作します:

template<typename T> 
struct Bar : PoolObj<Bar<T>> 
{ 
    static Storage<Bar<T>, 10> pool; 
}; 

template<typename T> 
Storage<Bar<T>, 10> Bar<T>::pool {}; 

int main() 
{ 
    Foo* f = new Foo(); 
    delete f; 
    return 0; 
} 
$ ./a.out 
new T 
delete T 

が、しかし、私は今PoolObjクラステンプレートを有効にしようとしています

これは私が次のエラーを取得するコンパイルしようとすると

int main() 
{ 
    Bar<int>* b = new Bar<int>(); 
    delete b; 
    return 0; 
} 

を動作しません:

In instantiation of ‘struct Storage<Bar<int>, 10ul>’: 
    required from ‘struct Bar<int>’ 
error: invalid application of ‘sizeof’ to incomplete type ‘Bar<int>’ 
    std::aligned_storage_t<sizeof(T), alignof(T)> data[N]; 
  • はなぜFooのための完全なStorageTですが、Bar<int>らのために不完全な?
  • 私がここで望んでいるデザインを達成することは可能でしょうか?以下

全例:(and on coliru)

#include <type_traits> 
#include <cstddef> 

template<typename T, std::size_t N> 
struct Storage 
{ 
    std::aligned_storage_t<sizeof(T), alignof(T)> data[N]; 
}; 

template<typename T> 
struct PoolObj 
{ 
    static void* operator new(std::size_t size) 
    { 
     return &T::pool.data[0]; 
    } 

    static void operator delete(void* p, std::size_t size) 
    { 
    } 
}; 

struct Foo : PoolObj<Foo> 
{ 
    static Storage<Foo, 10> pool; 
}; 

Storage<Foo, 10> Foo::pool {}; 

template<typename T> 
struct Bar : PoolObj<Bar<T>> 
{ 
    static Storage<Bar<T>, 10> pool; 
}; 

template<typename T> 
Storage<Bar<T>, 10> Bar<T>::pool {}; 

int main() 
{ 
    Foo* f = new Foo(); 
    delete f; 

    Bar<int>* b = new Bar<int>(); 
    delete b; 

    return 0; 
} 

編集:

興味深いことにthis works fine in clang (coliru)

  • どのコンパイラが正しいですか?
  • これはgccのバグですか?

第二編集:

のコメントによると、それはあまりにもVS2017で見つける動作します。そのように、私はgccのバグに傾いていると思いますか?

+0

これは、Bar の定義の一部としてBar を使用しているためです。正式な回答にするには十分ではありません。しかし、エラーは理にかなっています。 – OriBS

+0

コンパイラが 'Bar 'の定義中に静的メンバープールのサイズを理解しようとすると、この時点で 'Bar 'は不完全です。プールのサイズを理解するために 'std :: aligned_storage_t 'をチェックすると、 'Bar '(この文脈ではT)はまだ不完全であり、sizeofの不完全な型を取ることは不正です。 – OriBS

+0

@OriBS 'Storage 012 'は、' Bar 'と宣言したときに、' Bar 'に対してのみインスタンス化されます。それがインスタンス化される時点で、それは完全な型を確実に持っていますか?さらに、それはclangの下で動作します - どのコンパイラが正しいですか? –

答えて

1

どのコンパイラが正しいですか?

は、一般的に言えば、関連する文言は

なければならないクラステンプレート特殊化の暗黙のインスタンスは、宣言の暗黙のインスタンス生成を引き起こすなくの[temp.inst-2]スコープメンバーの列挙体、静的データメンバー、メンバーテンプレート、およびフレンドの定義、デフォルト引数、またはnoexcept指定子。

[temp.inst-3]クラステンプレートまたはメンバテンプレートのメンバーは、明示的にインスタンス化または明示的に特殊化されていない限り、特殊が参照されたときに部材の特殊化が暗黙的にインスタンス化されますメンバー定義が存在することを必要とするコンテキストで、またはメンバーの定義の存在がプログラムのセマンティクスに影響を与える場合、 スタティックデータメンバの定義が存在するためにスタティックデータメンバ自体が使用されていない限り、スタティックデータメンバの初期化(および関連する副作用)は発生しません。

staticメンバー変数は宣言ですが、定義ではないので、clangが正しいです。そして「存在するメンバー定義を必要とするか、またはメンバーの定義の存在が影響する場合、コンテキスト「宣言ではなく、定義をインスタンス化する」何を決定する際に、両方のコンパイラが乱暴に振る舞う、と述べ

プログラムの意味 ""(最近、thisのようなコーナーケースがたくさんあります)。回避策として


、あなたが

template<typename T> 
struct Bar : PoolObj<Bar<T>> 
{ 
    static Storage<Bar<T>, 10>&& pool; 
}; 

template<typename T> 
Storage<Bar<T>, 10>&& Bar<T>::pool = Storage<Bar<T>, 10>{}; // note, the temporary is lifetime-extended here 

代わりに静的参照を使用することができ、これは(それがあるべきとして)aligned_storageをインスタンス化を避けるために打ち鳴らすとgccの両方を説得するようです。

+0

生涯延長付きニートトリック! –

関連する問題