CRTP - verification from the base class that the derivative meets the requirements

A curiously repeating pattern template can be used to implement a kind of static polymorphism. For example:

#include <iostream> template< class Derived > struct Base { static void print ( ) { std::cout << Derived::number_to_print << '\n'; } }; struct Derived final : Base< Derived > { static constexpr unsigned int number_to_print = 27; }; int main ( ) { Derived::print(); } 

It behaves as expected and prints 27 .

Now I would like to add checks to the base class to claim that derived classes meet certain requirements. In the above example, such checks may be:

 #include <iostream> #include <type_traits> template< class Derived > struct Base { // --- Checks begin static_assert( std::is_same< decltype(Derived::number_to_print), unsigned int >::value, "static member `number_to_print' should be of type `unsigned int'" ); // --- Checks end static void print ( ) { std::cout << Derived::number_to_print << '\n'; } }; struct Derived final : Base< Derived > { static constexpr unsigned int number_to_print = 27; }; int main ( ) { Derived::print(); } 

This does not work, because at the moment when the Base<Derived> is created, Derived declared only forward, i.e. is incomplete, and none of this is yet known, except that it is a struct.

I scratch my head, as I believe that these checks may be useful for users of the base class, but have not found a way to do this.

Is this possible ?, and if so, how?

+7
c ++ c ++ 14 crtp
source share
3 answers

As noted by Kerrek SB , you can move a static statement to the body of a member function.

And when you provide functionality as non- static member functions instead of the current static member function, you can let the asserting body be the constructor body.

eg.

 #include <iostream> #include <type_traits> using namespace std; #define STATIC_ASSERT( e ) static_assert( e, #e " // <- is required" ) template< class A, class B > constexpr auto is_same_() -> bool { return std::is_same<A, B>::value; } template< class Derived > struct Base { Base() { STATIC_ASSERT(( is_same_< remove_cv_t< decltype( Derived::number_to_print ) >, int >() )); } void print() const { std::cout << Derived::number_to_print << '\n'; } }; struct Derived final : Base< Derived > { static constexpr int number_to_print = 27; }; auto main() -> int { Derived::Base().print(); } 
+4
source share

As a dirty trick, you can transfer a static statement into the body of a member function. Since member function definitions effectively appear immediately after the class definition, the Derived type is completed inside the member function body.

Beware that member functions of class templates themselves are function templates, and therefore they are only created when used (or if the template is explicitly created).

+3
source share

As an alternative approach (other answers are really good), you can use a private method and rely on sfinae:

 #include <iostream> #include <type_traits> template<class Derived> struct Base { static void print ( ) { print<Derived>(); } private: template<class D> static std::enable_if_t<std::is_same<std::remove_cv_t<decltype(D::N)>, unsigned int>::value> print() { std::cout << D::N << '\n'; } }; struct Derived final: Base<Derived> { static constexpr unsigned int N = 27; }; int main ( ) { Derived::print(); } 

Thus, the error only occurs if you are really using print .

+1
source share

All Articles