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;

限制

该检查还会在某些情况下建议修复建议。