bugprone-crtp-constructor-accessibility¶
检测容易出错的奇怪循环模板模式(CRTP)用法,当 CRTP 可以在自身和派生类之外构造时。
CRTP 是一种习惯用法,其中一个类从一个模板类派生,它本身是模板参数。应该确保如果一个类旨在成为这种习惯用法中的基类,它只能在派生类是其模板参数时实例化。
示例
template <typename T> class CRTP {
private:
CRTP() = default;
friend T;
};
class Derived : CRTP<Derived> {};
下面可以看到一些会导致这种习惯用法失效的常见错误。
如果打算在 CRTP 中使用的类的构造函数是公有的,那么它允许用户单独构造该类。
示例
template <typename T> class CRTP {
public:
CRTP() = default;
};
class Good : CRTP<Good> {};
Good GoodInstance;
CRTP<int> BadInstance;
如果构造函数是受保护的,则可以防止意外实例化,但是当使用不同的类作为模板参数而不是派生类时,它可能会隐藏错误。
示例
template <typename T> class CRTP {
protected:
CRTP() = default;
};
class Good : CRTP<Good> {};
Good GoodInstance;
class Bad : CRTP<Good> {};
Bad BadInstance;
为了确保不会发生意外实例化,最佳实践是将构造函数设为私有并声明派生类为友元。请注意,作为权衡,这也使派生类可以访问 CRTP 的所有其他私有成员。
示例
template <typename T> class CRTP {
CRTP() = default;
friend T;
};
class Good : CRTP<Good> {};
Good GoodInstance;
class Bad : CRTP<Good> {};
Bad CompileTimeError;
CRTP<int> AlsoCompileTimeError;
限制
在 C++11 以下版本中不支持该检查
该检查不处理将派生类作为可变参数模板参数传递的情况
不会检查可以构造 CRTP 的可访问函数,例如工厂函数
该检查还会在某些情况下建议修复建议。