0

I have a class that receives its base type as a template arg and I want my derived class to call a function, print. This function should use the derived implementation by default but if the base class has a print function it should use the base implementation.

#include <iostream>

class BaseWithPrint {
  public:
    static void print(int i) { std::cout << "Base::print\n"; }
};

class BaseWithoutPrint {
};

template <typename B>
class Derived : public B {
  public:
    static void print(bool b) { std::cout << "Derived::bool_print\n"; }

    template <typename T>
    static void print(T t) { std::cout << "Derived::print\n"; }
    
    void Foo() {
        print(1);
        print(true);
        print("foo");
    }
};

int main()
{
    Derived<BaseWithPrint> d1;
    d1.Foo();
    
    
    Derived<BaseWithoutPrint> d2;
    d2.Foo();

    return 0;
}

This code only ever calls the Derived version of print.

Code can be seen at

https://onlinegdb.com/N2IKgp0FY

Benjy Kessler
  • 7,356
  • 6
  • 41
  • 69
  • 1
    I guess your actual CRTP would be some further derived class from `Derived<>`. If that is so, you will need to use SFINAE in the `Derived::print(T t)` function, to choose the `B` implementation or the "default" one in `Derived` – LoPiTaL Apr 08 '22 at 09:59
  • "This code only ever calls the Derived version of print" - Yup. that is hyper-accurate. No attempt is ever made anywhere in this code to invoke `B::print`, so naturally it will never be called. This ins't a [CRTP](https://stackoverflow.com/questions/4173254/what-is-the-curiously-recurring-template-pattern-crtp). A CRTP will derive from a base template subordinated by the derived class. E.g. class Derived : public Base. – WhozCraig Apr 08 '22 at 09:59
  • Oh, right, I completely confused CRTP. I'll edit that out. – Benjy Kessler Apr 08 '22 at 10:02
  • https://stackoverflow.com/questions/257288/templated-check-for-the-existence-of-a-class-member-function – pptaszni Apr 08 '22 at 10:38

1 Answers1

1

If you know that the base class will have some kind of print, then you can add using B::print to your derived class. If a perfect match isn't found in the derived, then it'll check the base.

Demo

To handle it for the case where there may be a base print, I think you need to resort to SFINAE. The best SFINAE approach is really going to depend on your real world situation. Here's how I solved your example problem:

template <class T, class = void>
struct if_no_print_add_an_unusable_one : T {
    // only ever called if derived calls with no args and neither
    // the derived class nor the parent classes had that print.
    // ie. Maybe best to force a compile fail:
    void print(); 
};

template <class T>
struct if_no_print_add_an_unusable_one <T, decltype(T().print(int()))> : T {};

//====================================================================


template <class B>
class Derived : public if_no_print_add_an_unusable_one<B> {

    using Parent = if_no_print_add_an_unusable_one<B>;
    using Parent::print;

public:

    // ... same as before
};

Demo

Elliott
  • 2,603
  • 2
  • 18
  • 35