CRTP中依赖类型何时为完整类型?嵌套模板编译疑问解析
CRTP单例实现:嵌套模板写法的合法性与类型完整时机解析
问题场景
我尝试用Mixin类结合**奇异递归模板模式(CRTP)**实现单例,让类拥有一个存储自身实例的静态数据成员,且该成员知晓类的完整大小。初始写法如下:
template<class ObjectType> class SingletonOf { static inline char instance_memory[sizeof(ObjectType)]; // 编译报错:ObjectType是不完整类型 public: void *operator new(std::size_t) { return &instance_memory; } void operator delete(void *) { } }; class foo : public SingletonOf<foo> // CRTP:让SingletonOf知晓foo类型 { // foo的数据成员与成员函数 }; void bar() { foo *p = new foo; // 预期调用SingletonOf<foo>::operator new返回实例内存 }
在C20中,上述代码会因ObjectType(即foo)是不完整类型而报错。但改用嵌套类模板的写法后,可通过clang的C++20模式编译:
template<class ObjectType> class SingletonOf { template<class CompleteObjectType> struct InstanceMemory { static char inline instance_memory[sizeof(CompleteObjectType)]; }; public: void *operator new(std::size_t) { return InstanceMemory<ObjectType>::instance_memory; } // 其他成员实现... }; class foo : public SingletonOf<foo> { // foo的数据成员与成员函数 };
需要明确三个问题:
- 这种嵌套模板写法是否合法?
- 为何此时
ObjectType会被视为完整类型? - CRTP中,依赖类型名何时会成为完整类型?
1. 嵌套模板写法的合法性
该写法完全符合C++标准,是合法的实现方式。关键在于嵌套模板InstanceMemory的实例化时机被延迟,直到需要使用它的代码执行时,此时依赖的ObjectType已经是完整类型,满足sizeof操作对完整类型的要求。
2. 为何此时ObjectType被视为完整类型
初始写法的问题在于时机不对:
- 当
foo继承SingletonOf<foo>时,SingletonOf<foo>这个基类模板会立即被实例化,但此时foo的定义还没完成(还在处理基类列表和后续的成员定义),属于不完整类型,直接用sizeof(ObjectType)违反了C++标准中“不能对不完整类型使用sizeof”的规则,因此编译报错。
而嵌套模板的写法巧妙地推迟了求值时机:
SingletonOf<foo>实例化时,只会处理类的声明部分,成员函数operator new的函数体不会被立即实例化。- 只有当
bar()中执行new foo时,operator new的函数体才会被实例化——此时foo的整个定义已经完成(bar函数在foo定义之后),ObjectType(即foo)已经是完整类型,InstanceMemory<foo>的实例化可以合法调用sizeof(foo)。
简单说,嵌套模板把sizeof的求值从基类模板实例化阶段推迟到了成员函数实际调用阶段,避开了派生类不完整的时间段。
3. CRTP中依赖类型名的完整时机
在CRTP场景下,派生类(即依赖类型ObjectType)的完整时机遵循以下规则:
- 基类模板实例化时:当派生类继承CRTP基类时,派生类处于不完整状态——此时基类模板的类声明部分会被实例化,但成员函数的函数体不会被实例化(除非被显式实例化或调用)。
- 派生类定义完成后:当派生类的所有成员、基类列表都处理完毕后,任何在派生类定义之后引用它的代码中,派生类都会被视为完整类型。
- 成员函数实例化时:基类模板中的成员函数(比如
operator new)的函数体,会在第一次被调用时实例化。如果这个调用发生在派生类定义之后,那么此时ObjectType就是完整类型。
注意:如果基类的成员函数是constexpr、consteval,或者被显式实例化,函数体的实例化会提前,此时仍可能遇到不完整类型的问题——但你的写法中operator new是普通成员函数,仅在调用时实例化,因此不会有问题。
内容的提问来源于stack exchange,提问作者cs-
相关产品推荐
相关产品推荐

