33

I'm having a little issue with overriding static methods of base clases, but the whole question is very complicated and too long (generalization of resource management in game engine), so here's a simplified version:

template<class T>
class base
{
    static void bar()
    { printf("bar"); }
public:
    static void foo()
    { bar(); }
};

class derived : public base<int>
{
    static void bar()
    { printf("baz"); }
};

int main() { derived::foo(); }

The code above outputs "bar" in my case, insted I want it to output "baz". How can I go about that? It seems that no matter what I attempt, base::foo() always calls base::bar(). I might have an issue with the design. I've never came across this problem - how can I resolve it?

Biser Krustev
  • 443
  • 1
  • 4
  • 6
  • You can't override static methods. You can overload them (which is what you're doing here), but you're just getting static dispatch. I guess you could use the [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). – TartanLlama Dec 11 '15 at 11:41
  • 1
    Possible duplicate of [Alternative to c++ static virtual methods](https://stackoverflow.com/questions/2721846/alternative-to-c-static-virtual-methods) – jww Jun 11 '17 at 16:08

1 Answers1

68

What you're trying to do is not achievable with simple class inheritance; a method cannot be both static and virtual.

You need a static method to be able to call a function without an object (an instance); and you need bar to be virtual so that bar<int>::foo() calls derived::bar() when called from a derived instance.

Those two traits are mutually exclusive. But the Curiously Recursive Template Pattern (CRTP) may be a solution here:

#include <iostream>

template<class T>
struct base
{
    static void foo()
    {
        T::bar();
    }
};

struct derived : public base<derived>
{
    static void bar()
    {
        std::cout << "derived" << std::endl;
    }
};

int main()
{
    derived::foo();
}

Live example

Adam.Er8
  • 12,675
  • 3
  • 26
  • 38
YSC
  • 38,212
  • 9
  • 96
  • 149
  • Thank you very much! Your answer helped me! – ryo Feb 02 '17 at 12:06
  • 1
    This answer blew my mind. How does the complier know that there will be a method bar() on class T? Does it statically check every instance of base to make sure it fits the usage? – mayacoda Mar 27 '18 at 15:05
  • 9
    @MayaNedeljkovich It doesn't. Anytime a specialization of `base::foo` is made (in the example program, the exact time `base::foo` is called), it _does_ check that `base::foo` make sense for the given `T`. This is the so-called template second pass compilation (aka "in deduced context"). If it was not the case, => compilation error. [Try it by yourself](http://coliru.stacked-crooked.com/a/4468b8dcdd5d54c7). – YSC Mar 27 '18 at 15:08
  • 1
    @YSC: +1 for providing live examples two times! – trapicki Jul 11 '20 at 13:30
  • @MayaNedeljkovich Typically in rust we can use the trait bound on the template arguments. In cpp20, there seems a similar concept named "concept", I'm not a master of C++ template, but I think the stdlib could help before cpp20. – Forsworn Oct 13 '22 at 01:56