[c++] C++ static virtual members?

This question is over a decade old, but it looks like it gets a good amount of traffic, so I wanted to post an alternative using modern C++ features that I haven't seen anywhere else.

This solution uses CRTP and SFINAE to perform static dispatching. That, in itself, is nothing new, but all such implementations I've found lack strict signature checking for "overrides." This implementation requires that the "overriding" method signature exactly matches that of the "overridden" method. This behavior more closely resembles that of virtual functions, while also allowing us to effectively overload and "override" a static method.

Note that I put override in quotes because, strictly speaking, we're not technically overriding anything. Instead, we're calling a dispatch method X with signature Y that forwards all of its arguments to T::X, where T is to the first type among a list of types such that T::X exists with signature Y. This list of types considered for dispatching can be anything, but generally would include a default implementation class and the derived class.

Implementation

#include <experimental/type_traits>

template <template <class...> class Op, class... Types>
struct dispatcher;

template <template <class...> class Op, class T>
struct dispatcher<Op, T> : std::experimental::detected_t<Op, T> {};

template <template <class...> class Op, class T, class... Types>
struct dispatcher<Op, T, Types...>
  : std::experimental::detected_or_t<
    typename dispatcher<Op, Types...>::type, Op, T> {};


// Helper to convert a signature to a function pointer
template <class Signature> struct function_ptr;

template <class R, class... Args> struct function_ptr<R(Args...)> {
    using type = R (*)(Args...);
};


// Macro to simplify creation of the dispatcher
// NOTE: This macro isn't smart enough to handle creating an overloaded
//       dispatcher because both dispatchers will try to use the same
//       integral_constant type alias name. If you want to overload, do it
//       manually or make a smarter macro that can somehow put the signature in
//       the integral_constant type alias name.
#define virtual_static_method(name, signature, ...)                            \
    template <class VSM_T>                                                     \
    using vsm_##name##_type = std::integral_constant<                          \
        function_ptr<signature>::type, &VSM_T::name>;                          \
                                                                               \
    template <class... VSM_Args>                                               \
    static auto name(VSM_Args&&... args)                                       \
    {                                                                          \
        return dispatcher<vsm_##name##_type, __VA_ARGS__>::value(              \
            std::forward<VSM_Args>(args)...);                                  \
    }

Example Usage

#include <iostream>

template <class T>
struct Base {
    // Define the default implementations
    struct defaults {
        static std::string alpha() { return "Base::alpha"; };
        static std::string bravo(int) { return "Base::bravo"; }
    };

    // Create the dispatchers
    virtual_static_method(alpha, std::string(void), T, defaults);
    virtual_static_method(bravo, std::string(int), T, defaults);
    
    static void where_are_the_turtles() {
        std::cout << alpha() << std::endl;  // Derived::alpha
        std::cout << bravo(1) << std::endl; // Base::bravo
    }
};

struct Derived : Base<Derived> {
    // Overrides Base::alpha
    static std::string alpha(){ return "Derived::alpha"; }

    // Does not override Base::bravo because signatures differ (even though
    // int is implicitly convertible to bool)
    static std::string bravo(bool){ return "Derived::bravo"; }
};

int main() {
    Derived::where_are_the_turtles();
}