以下是我正在尝试创建的池化对象的最小工作示例(显然不是完整的功能 - 我只是想说明我遇到的问题)
我有一个类模板Storage
,其中包含aligned_storage
:
template<typename T, std::size_t N>
struct Storage
{
std::aligned_storage_t<sizeof(T), alignof(T)> data[N];
};
然后我有一个基类模板PoolObj
,它使用T
从模板参数operator new
的静态类成员中分配:
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
的类,并且有一个静态Storage
成员pool
,所以当我使用new
创建实例时,我会得到从池中存储。
struct Foo : PoolObj<Foo>
{
static Storage<Foo, 10> pool;
};
Storage<Foo, 10> Foo::pool {};
这一切都很好:
int main()
{
Foo* f = new Foo();
delete f;
return 0;
}
$ ./a.out new T delete T
但是,现在我正在尝试制作一个PoolObj
启用的类模板:
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()
{
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];
T
Storage
完成Foo
,Bar<int>
et al?以下完整示例: (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)。
第二次编辑:
根据评论,它也适用于VS2017。因此,我想我倾向于gcc中的一个错误?
答案 0 :(得分:1)
哪种编译器正确?
一般来说,相关的措辞应该是
[temp.inst-2] 类模板特化的隐式实例化会导致声明的隐式实例化,但不是定义,默认参数或类成员函数,成员类,作用域成员枚举,静态数据成员,成员模板和朋友的noexcept-specifiers
和
[temp.inst-3] 除非已明确实例化或明确专门化类模板或成员模板的成员,否则在引用特化时隐式实例化成员的特化。需要成员定义存在的上下文,或者成员定义是否存在影响程序语义的上下文;特别是,除非静态数据成员本身以需要静态数据成员的定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。
静态成员变量是声明但不是定义,所以clang是对的。
也就是说,两个编译器在决定什么&#34; 来实例化一个声明而不是一个定义&#34;时,表现得很疯狂。和&#34; 需要成员定义存在的上下文,或者成员定义是否存在影响程序语义的上下文&#34;意思是(你可以在SO上找到很多极端情况,比如最近的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
这似乎说服clang和gcc避免实例化aligned_storage(应该是这样)。