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

时间:2017-11-23 20:08:38

标签: c++ templates

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

我有一个类模板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完成FooBar<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)

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

第二次编辑:

根据评论,它也适用于VS2017。因此,我想我倾向于gcc中的一个错误?

1 个答案:

答案 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(应该是这样)。