11
#include <iostream>

template<typename Impl>
struct renderer{
    void get(){
        static_cast<Impl*>(this)->get();
    }
};
struct open_gl : public renderer<open_gl>{
    void get(){
        std::cout << "OpenGL" << std::endl;
    }
};
struct direct_draw : public renderer<direct_draw>{
    void get(){
        std::cout << "DX" << std::endl;
    }
};
template<typename T>
void print_renderer(renderer<T> r){
    r.get();
}
int main() {
    auto gl = open_gl();
    auto dx = direct_draw();
    print_renderer(gl);
    print_renderer(dx);
}
  1. Why can't I change the parameter of print_renderer to void print_renderer(const renderer<T> &r)? cannot convert 'this' pointer from 'const renderer<open_gl>' to 'renderer<open_gl> &' `

  2. Why do I get a runtime error when I rename the method get in open_gl from get to get1? Shouldn't this trigger a compiler error? Error = Stack overflow

**Note I am using the latest MSVC

quantdev
  • 23,517
  • 5
  • 55
  • 88
Maik Klein
  • 15,548
  • 27
  • 101
  • 197

2 Answers2

12

1) Because get is not a const member function : it cannot make the promise of not modify your (const) argument.

You could declare get as const, and it compiles fine :

void get() const { ... }

2) The base get method will be called, going into infinite recursion : Stack Overflow.

If you declare your function override (it needs to be virtual), the compiler will throw an error if it does not indeed override a base method :

void get1() override  { ... } // Compiler error
void get() override   { ... } // Ok

Note:

The title is "Static polymorphism in C++", but I think that you misunderstood what is static polymorphism : it does not (have to) make use of inheritance (as you did). Rather, the templates compile-time duck typing will statically "resolve" function calls for you.

That is, you don't need related types, you don't need the base renderer class at all, and you can simply do the following (in which case, renaming to get1 will cause a compiler error) :

#include <iostream>

struct open_gl {
    void get(){
        std::cout << "OpenGL" << std::endl;
    }
};
struct direct_draw {
    void get(){
        std::cout << "DX" << std::endl;
    }
};

template<typename T>
void print_renderer(T r){
    r.get();
}

int main() {
    auto gl = open_gl();
    auto dx = direct_draw();
    print_renderer(gl);
    print_renderer(dx);
}

Live demo

quantdev
  • 23,517
  • 5
  • 55
  • 88
  • Thanks, about my second question. Is it possible to throw a compiler error when `get` is not implemented? Something like override? – Maik Klein Aug 28 '14 at 22:57
  • How can I make `void get()` virtual if I want static polymorphism? – Maik Klein Aug 28 '14 at 23:08
  • 1
    @MaikKlein then you'll have to use templates machinery to statically check for the existence of `get` in a given type. Check [this anser](http://stackoverflow.com/a/10958151/3510483). – quantdev Aug 28 '14 at 23:11
  • 1
    @quantdev: Or, (my favorite) give the `renderer` members slightly different names. Such as `void renderer::get(){static_cast(this)->get_();}`, and then you always get compiler errors if the function is not implemented in the derived class properly. – Mooing Duck Aug 28 '14 at 23:17
  • @quantdev I wasn't sure what the best practices are. Either I use templates with ducktyping and maybe do an assert `static_assert(is_renderer,"")` or I do it with inheritance and CRTP. I'll create another question on SO for this. – Maik Klein Aug 29 '14 at 10:33
5
  1. Becuase get is not marked const.
  2. Because the base class method is used (irrelevantly of cast), and it goes into infinite loop.
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135