2023年4月3日 星期一

C++:簡介 Curiously Recurring Template Pattern (CRTP)

本文為 C++ Software Design 書的第 26 節內容。

CRTP 的目的是提供編譯時期對於不同類別的抽象。以下為 base class 的例子:
template <typename Derived>
struct DenseVector {
public:
  Derived& derived() { return static_cast<Derived&>(*this); }

  Derived const% derived() const { return static_cast<Derived const&>(*this); }

  size_t size() const { return derived().size(); }

  decltype(auto) operator[](const size_t index) { return derived()[index]; }

  decltype(auto) begin() { return derived().begin(); }
  decltype(auto) end() { return derived().end(); }
}
上面 base class 即為 DenseVector 的抽象。接下來要實作 derived class:
template <typename T>
class DynamicVector : public DenseVector<DynamicVector<T>>
{
public:
  size_t size() const;
}
這樣就完成了 Dynamic Vector 與 DenseVector 之間在編譯時期的繼承關係。要注意的是 decltype(auto) 的使用:這樣就不用在 base class 去推敲要用哪一種型態。 在 C++ 20 引入 Concept 的觀念後,可以利用 Concept 與 tag class 來實作 CRTP 的 base class:
struct DenseVectorTag {};

template <typename T>
struct IsDenseVector : public std::is_base_of<DenseVectorTag, T> {};

template <typename T>
constexpr bool IsDenseVector_v = IsDenseVector<T>::value;

template <typename T>
concept DenseVector =
  /* concepts */
  && IsDenseVector_v<T>;
這個 tag class 的用途是確保指有需要的類別會符合 DenseVector 的 concept,其他的類別都不會實作出來。而 derived class 有兩種實作的方法:
// Intrusively
template <typename T>
class DynamicVector : private DenseVectorTag
{
  // implementations
};

// Non-intrusively
template <typename T>
class StaticVector
{
  // implementations
};

template <typename T>
struct IsDenseVector<StaticVector<T>> : public std::true_type {};

沒有留言:

張貼留言