Search code examples
c++templatesstatic-methodspure-virtual

A way to guarantee that a bunch of derived classes have static functions that do the same thing and that are guaranteed to be named in the same way?


I know that it isn't possible to have class methods that are both pure virtual and static (see this discussion). So, I ask: is there a way to guarantee that a bunch of derived classes have static functions that do the same thing and that are guaranteed to be named in the same way?

Consider the following example:

#include <iostream>
#include <array>

// class base {
// public:
//  virtual static constexpr size_t nums() = 0
// }

template<size_t num>
class derived1 {//: public base {
public:
    static constexpr size_t nums() {return num;}
    derived1()  {}
};

template<size_t num>
class derived2 {// : public base {
public:
    static constexpr size_t nums() {return num;}
    derived2()  {}
};

int main() {

    std::cout << derived2<20>::nums() << "\n";
    return 0;
}

I commented out the base class so that it compiles, but I want an interface class that guarantees a bunch of derived classes all have nums() named in the same way. In this particular example nums is named the same way in both derived class, but if I write a third derived class a few years from now, and I forget this convention, I don't want to be able to compile anything, ideally.


Solution

  • You could use CRTP as the base class, and then have a routine that depends on the derived class having implemented the expected static function.

    Here's an example, and I have thunk_nums in the base class dependent on the derived class having nums, although they could have been named the same.

    Note that if a function isn't used, it won't be generated, and so won't trigger an error. (Which may be desired behavior, or if not desired then steps would have to be taken to ensure it is generated.)

    #include <iostream>
    
    template <typename DERIVED>
    class base {
    public:
        static constexpr size_t thunk_nums() {
            return DERIVED::nums();
        }
    };
    
    template<size_t num>
    class derived1 : public base<derived1<num>> {
    public:
        static constexpr size_t nums() {return num;}
        derived1() {}
    };
    
    template<size_t num>
    class derived2 : public base<derived2<num>> {
    public:
        static constexpr size_t nums() {return num;}
        derived2() {}
    };
    
    int main() {
        std::cout << derived2<20>::thunk_nums() << "\n";
    }