Last active
March 16, 2022 09:33
-
-
Save dboyliao/95f827d584df1c8b66cb8d0fe55b6725 to your computer and use it in GitHub Desktop.
Simple C++ Example for CRTP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <algorithm> | |
| #include <cmath> | |
| #include <cstdint> | |
| #include <iostream> | |
| #include <utility> | |
| template <class Derived> | |
| class PointBase { | |
| public: | |
| constexpr static const double PI = 3.14159265358979323846; | |
| PointBase(float v0, float v1) : m_v0(v0), m_v1(v1) { | |
| std::cout << "PointBase: " << this << std::endl; | |
| } | |
| float const &v0() const { return m_v0; } | |
| float &v0() { return m_v0; } | |
| float const &v1() const { return m_v1; } | |
| float &v1() { return m_v1; } | |
| float dist() const { | |
| // Prevent the derived class from working if it doesn't define dist(), | |
| // just like what a pure virtual function does. | |
| static_assert(&PointBase<Derived>::dist != &Derived::dist, | |
| "derived class must define dist()"); | |
| return static_cast<const Derived *>(this)->dist(); | |
| } | |
| private: | |
| Derived const &derived() const { return *static_cast<Derived const *>(this); } | |
| float m_v0, m_v1; | |
| private: | |
| // safe guard for invalid inheritance | |
| PointBase() {} | |
| friend Derived; | |
| }; /* end class PointBase */ | |
| template <typename Derived> | |
| float use_dist(const PointBase<Derived> &pt) { | |
| return pt.dist(); | |
| } | |
| class CartesianPoint : public PointBase<CartesianPoint> { | |
| public: | |
| using PointBase<CartesianPoint>::PointBase; | |
| float dist() const { return std::hypot(v0(), v1()); } | |
| }; /* end class CartesianPoint */ | |
| class PolarPoint : public PointBase<PolarPoint> { | |
| public: | |
| using PointBase<PolarPoint>::PointBase; | |
| float dist() const { return std::abs(v0()); } | |
| }; /* end class PolarPoint */ | |
| class BadPoint : public PointBase<BadPoint> { | |
| public: | |
| using PointBase<BadPoint>::PointBase; | |
| // Intentionally omit dist(). | |
| /*float dist() const | |
| { | |
| return 0.0; | |
| }*/ | |
| }; /* end class BadPoint */ | |
| // intentionally iheritant from wrong base class | |
| class BadBadPoint : public PointBase<BadPoint> { | |
| public: | |
| using PointBase<BadPoint>::PointBase; | |
| }; | |
| int main(int, char **) { | |
| std::cout << "sizeof(CartesianPoint) = " << sizeof(CartesianPoint) | |
| << std::endl; | |
| std::cout << "sizeof(PolarPoint) = " << sizeof(PolarPoint) << std::endl; | |
| CartesianPoint cpt(1, 1); | |
| PolarPoint ppt(1, PolarPoint::PI / 4); | |
| std::cout << "CartesianPoint(1,1)::dist() = " << use_dist(cpt) << std::endl; | |
| std::cout << "PolarPoint(1, pi/4)::dist() = " << use_dist(ppt) << std::endl; | |
| // This won't compile | |
| // error: static_assert failed "derived class must define dist()" | |
| // BadPoint bp; | |
| // use_dist(bp); | |
| // This won't compile | |
| // since the default constructor is not accessible to BadBadPoint (due to | |
| // incorrect usage of CRTP) | |
| // BadBadPoint bbp; | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment