静态类模板成员:将"sizeof"应用于不完整类型无效

static class template member: invalid application of ‘sizeof’ to incomplete type

本文关键字:quot 类型 无效 应用于 sizeof 静态类 成员      更新时间:2023-10-16

这是我尝试创建的池化对象的最小工作示例(显然功能不完整 - 我只是想说明我遇到的问题)

我有一个类模板Storage其中包含aligned_storage

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

然后我有一个基类模板PoolObj它使用operator new从模板参数T的静态类成员中分配出来:

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

现在我有一个继承自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];
  • 为什么TFoo来说是Storage完整的,而对Bar<int>等人来说是不完整的?
  • 是否有可能实现我在这里希望的设计?

完整示例如下:(和大肠杆菌)

#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;
}

编辑:

有趣的是,这在叮当(coliru)中工作正常。

  • 哪个编译器是正确的?
  • 这是 gcc 中的错误吗?

第2次编辑:

根据评论,它在VS2017中也可以找到。因此,我想我倾向于 gcc 中的错误?

哪个编译器是正确的?

一般而言,相关措辞应为

[temp.inst-2]类模板专用化的隐式实例化会导致声明的隐式实例化,但不会导致类成员函数、成员类、作用域成员枚举、静态数据成员、成员模板和友元的定义、默认参数或 noexcept-specifier 的隐式实例化

[temp.inst-3]除非类模板或成员模板的成员已被显式实例化或显式专用化,否则当在需要成员定义的上下文中引用专用化时,或者如果成员定义的存在影响程序的语义,则隐式实例化成员的专用化;特别是, 静态数据成员的初始化(以及任何关联的副作用)不会发生,除非静态数据成员本身的使用方式要求存在静态数据成员的定义

静态成员变量是一个声明,而不是一个定义,所以 clang 是对的。

也就是说,在决定">实例化声明而不是定义"和"需要成员定义的上下文或成员定义的存在是否影响程序的语义"是什么意思时,两个编译器的行为都很疯狂(你可以在SO上找到很多极端情况,就像最近这样)。


作为一种解决方法,您可以改用静态引用

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(因为它应该是)。