0

I'm using inheritance only for the sake of code reuse, I never cast class to its base class. It is performance heavy program so I would like to avoid the use of virtual functions but I do not know how. Consider following code

class A{
public:
  void print(){
    std::cout << "Result of f() is: " << f() << std::endl;
  }

  virtual std::string f(){
    return "A";
  }
};

class B : public A{
public:
  virtual std::string f(){
    return "B";
  }
};

Would it be somehow possible to not use virtual function for function f() and not to reimplement function print() in the class B? I do not care that A is base class of B I just do not want to write down f() again. Probably inheritance is not the way to go, maybe templates can be used in smart way but I have no idea.

tom
  • 1,520
  • 1
  • 12
  • 26
  • 3
    Inheritance is not a great way of getting code reuse. –  Feb 06 '17 at 15:48
  • 2
    If you want to avoid virtual polymorphism, lookup the CRTP. Performance impacto of vtables is overratet in most cases though. – πάντα ῥεῖ Feb 06 '17 at 15:48
  • 6
    Echoing the previous commenter, beware premature optimization. You should verify via empirical testing that virtual methods are actually a significant bottleneck for your program before going to too much effort to get rid of them, otherwise you're at risk of butchering your program without getting any speedup in return. – Jeremy Friesner Feb 06 '17 at 15:51
  • 2
    What do you mean by performance heavy? Even in insanely real-time, safety-of-life code, we use virtual functions when they are the right tool for the job. The [JSF style guide](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwijydy26fvRAhUBGGMKHcWoDiQQFggfMAA&url=http%3A%2F%2Fwww.phaedsys.com%2Fprincipals%2Fprogrammingresearch%2Fprdata%2FJSF%2B%2B_%2520Rev_D_JUN07.pdf&usg=AFQjCNFbTCEFeajsueibf167eu71P2o-Yw&cad=rja) doesn't even patently reject virtual functions, and it's as conservative as a legitimate guide comes. – Nicolas Holthaus Feb 06 '17 at 15:52
  • I'm doing numerical simulation and some of these functions are nothing more then data access wrapper and I call them a lot. – tom Feb 06 '17 at 15:59
  • The function could just as well be a free function `void print(std::string value);` and be called as `print(f());` from any class. – Bo Persson Feb 06 '17 at 15:59
  • 1
    Have a read of _"Curiously Recurring Template Pattern"_ http://stackoverflow.com/questions/4173254/what-is-the-curiously-recurring-template-pattern-crtp – Richard Critten Feb 06 '17 at 16:06

3 Answers3

3

The CRTP pattern is typically used to avoid dynamic dispatch when it can be statically determined what implementation method to call for a particular method.

In your example, both A and B would inherit from a single base class, which provides the print() method. The base class, let's call it Print, is a template whose template argument is a class that provides f(). The twist that earned this pattern the "curious" moniker is that the subclasses must inherit the base class templatized over the subclass. This allows the subclasses to access the base class's print method, but obtaining a version of the base class - and by extension the version of print - that invokes their own f.

Here is an example of working code:

#include <iostream>

template<typename F>
class Print {
public:
  void print() {
    F& final = static_cast<F&>(*this);
    std::cout << "Result of f() is: " << final.f() << std::endl;
  }
};

class A: public Print<A> {
public:
  std::string f(){
    return "A";
  }
};

class B: public Print<B> {
public:
  std::string f(){
    return "B";
  }
};


int main() {
  A a;
  B b;
  a.print();
  b.print();
}

Although the print implementation is reused among A and B, there are no virtual methods here, nor is there virtual (run-time) dispatch or run-time checks. The one cast present is a static_cast<> whose safety is duly verified by the compiler.

This is possible because for every use of Print<F>, the compiler knows exactly what F is. Thus Print<A>::print is known to invoke A::f, while Print<B>::print invokes B::f, all known at compile time. This enables the compiler to inline and otherwise optimize such calls just like any other non-virtual method calls.

The downside is that there is no inheritance, either. Note that B is not derived from A - if it were, the pattern wouldn't work and both A::print and B::print would print A, since that's what Print<A> outputs. More fundamentally, you cannot pass B* where an A* is expected - that's undefined behavior. In fact, A and B don't share any common superclass, the Parent<A> and Parent<B> classes are completely separate. Loss of run-time dispatch, with both its drawbacks and benefits, and enabling static dispatch instead, is a fundamental tradeoff of static polymorphism.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • 1
    Great! This is exactly what I was looking for. – tom Feb 06 '17 at 16:39
  • @tom Have fun with C++! – user4815162342 Feb 06 '17 at 16:40
  • One question: Can the call `final.f()` be inlined? – tom Feb 06 '17 at 16:41
  • @tom Yes, because for every instantiation of `Print<>`, the compiler knows exactly what `static_cast` inside `Print::print` resolves to. In fact, being able to inline and otherwise statically analyze statically polymorphic is almost the whole point of using it. The ability to inline is also mentioned in the answer, at the end of the next-to-last paragraph. – user4815162342 Feb 06 '17 at 16:47
0

If, for whatever reason, you do not want the "overhead" of dynamic binding, you can omit the virtual-keyword, which makes the compiler use static binding and disabling polymorphism. That is, the compiler will bind the implementation solely based on the type of the variable at compile time, not at run time.

To be honest, I never were in the situation where I defined a specific implementation for a method in a subclass without enabling polymorphism. Doing so usually indicates that there is no specific behaviour, i.e. the method should not be overridden in the subclass at all.

Further, already the code encapsulating a character constant into a string object costs far more performance then the dynamic binding / vtable-"overhead". Really, rethink it twice and then measure the performance increase before doing such "optimisations".

Anyway, see how your code behaves if you omit virtual. Note that ptr->f() in the code is bound to A::f, because the type of the variable is A*, though it points to an object of type B:

class A{
public:
    void print(){
        std::cout << "Result of f() is: " << f() << std::endl;
    }

    std::string f(){
        return "A";
    }
};

class B : public A{
public:
    std::string f(){
        return "B";
    }
};

int main()
{
    A a; cout << a.f(); // -> yields "A"
    B b; cout << b.f(); // -> yields "B"
    A* ptr = &b; cout << ptr->f(); // -> yields "A"; (virtual f, in contrast) would be "B"

    return 0;
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
0

You could use templates to select dynamic or non-dynamic versions of A and B. A rather tricky/ugly option but worth considering.

#include <string>

template <bool Virt = false>
class A{
public:
  std::string f(){
    return "A";
  }
};

template <>
class A<true> : A<false>{
public:
  virtual std::string f(){
    return A<false>::f();
  }
};

template <bool Virt = false>
class B : public A<Virt>{
public:
  std::string f(){
    return "B";
  }
};

std::string f1() { return B<>().f(); }

std::string f2(A<true> &a) { return a.f(); }

std::string f3() { B<true> b; return f2(b); }

#include <iostream>

int main(){

std::cout << f1() << '\n';

std::cout << f3() << '\n';

return(0);
}

An interesting point to note with this, it would not be possible except for the debatable decision made very early on in C++ (pre-templates) that the virtual keyword should be optional when overriding.

WaltK
  • 724
  • 4
  • 13