酱紫模板?

内容纲要

以前不会C++,初中学了一点C++,觉得自己会了C++,上大学,又觉得自己不会C++了,模板元之类的真的博大精深...

正文

写代码的时候遇到了一个需求,我有一个类Referencable表示一个抽象的“可被引用”的类,这个类的实例可以通过反射式指针Ref引用,在实例析构时,所有指向该类的反射式指针在被调用时会表现为已经被清空为nullptr

我的具体做法是在Referencable里塞一个引用锚,引用锚保存一个指向Referencable的指针,在Referencable构造时创建引用锚,在析构时将引用锚内的指针清空(但不释放它)。然后重载Ref的拷贝、移动、指针等算符,在Ref里保存指向引用锚的指针,若发现其指向的引用锚指向nullptr则不再指向该引用锚。引用锚内保存一个指针计数器,保存指向它的Ref数量,在下降到0时析构自己。

为了方便,我将Ref写成了一个模板类,比若Ref<MaterialResource>表示指向MaterialResource的引用。但问题来了,我希望所有Ref可以接受的模板类参数都是Referencable的直接或间接儿子类。虽然可以在运行时通过RTTI进行检查,不过太消耗资源了,最好的做法是在编译的时候就X掉不合法的使用方式!怎么办呢?

C++11之后,模板的威力得到了极大扩展,我的实现是这样的:

class Referencable {};
class DataClass {} ;
class Derivative1 : public virtual Referencable {};
class Derivative2 : public virtual Referencable, public DataClass {};
class SubDerivative : public virtual Derivative1 {};
class AnotherClass {};

class CorrectUsage {public: inline constexpr CorrectUsage (int v) {} };
class WrongUsage {public: inline constexpr WrongUsage (int v) {} };

template<typename T, std::conditional_t<std::is_base_of_v<Referencable, T>, WrongUsage, CorrectUsage> usage = 0> class Ref {
  static_assert(std::is_same<decltype(usage), const CorrectUsage>::value, "Ref is only allowed to reference derivatives of Referencable!");
  public:
  Ref () {}
};

int main () {
  Ref<Derivative1>();       // OK
  Ref<Derivative2>();       // OK
  Ref<SubDerivative>();     // OK
  Ref<int>();               // Error
  Ref<AnotherClass>();      // Error
}

首先声明CorrectUsageWrongUsage两个类代表真和假,使用constexpr修饰构造函数让其能化为编译时确定的常量,在模板声明中使用conditional来决定第二个模板形参的类别,加上预设值让实际使用时不需要显示给出第二参数。在实例化模板时进行static_assert检查,如果有问题直接报错,报错信息也比较友好:

a.cpp: In instantiation of ‘class Ref<int>’:
a.cpp:26:12:   required from here
a.cpp:17:17: error: static assertion failed: Ref is only allowed to reference derivatives of Referencable!

以上代码需要使用C++20规范进行编译,如果是C++14以上,可以用其它基础类代替CorrectUsageWrongUsage,仍然能用。这个实现可能还是复杂了,应该有更简单的方法(但我不会),C++模板博大精深...

3.7

如果使用C++20,concept可以更好地完成以上功能。

template<typename T> concept DerivedFromReferencable = std::is_base_of_v<Referencable, T>;
template<DerivedFromReferencable T> class Ref { ... };
此条目发表在学习分类目录,贴了标签。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注