6

How would you go about filling-in a method if a base class doesn't provide it. I'd like to reuse the base class method if it is provided.

E.g.:

#include <iostream>
struct Base0 { };
struct Base1 { void m() { std::cout<<"Base1\n"; } };

template<typename T>
struct Derived : public T {
  //if T doesn't provide m, define it here, otherwise reuse the base class method 
  void m(){ /*? std::cout<<"Derived\n"; ?*/ }
};

int main(){
  Derived<Base0> d0;
  d0.m(); //should print "Derived"
  Derived<Base1> d1;
  d1.m(); //should print "Base1"
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Well, if you really like to do it this way, you got a problem with call order anyway. Derived is the type and as long as Derived has a function `m()` it will always overwrite the base function `m()` – Aslak Berby Feb 18 '16 at 20:40
  • You are mixing concepts: inheritance, virtual functions and templates. Altogether it is a code smell. Having templates, avoid inheritance and virtual functions. –  Feb 18 '16 at 20:44

3 Answers3

8

With SFINAE, you may do

template<typename T>
struct Derived : public T {
private:
  template <typename U = T>
  auto m_impl(int) -> decltype(std::declval<U&>().m()){ this->U::m(); }

  template <typename U = T>
  void m_impl(... ) { std::cout<<"Derived\n"; }

public:
  void m() { m_impl(0); }
};

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

In order to be general, you should define the function anyway under a different signature:

template<typename T>
struct Derived : public T
{
     auto m(std::false_type) { std::cout<<"Derived\n"; }
};

Then you can use the methods given in this thread in order to check whether the base class has the function m():

template <typename...>
using void_t = void;

template <typename T, template <typename> class D, typename = void>
struct detect : std::false_type {};

template <typename T, template <typename> class D>
struct detect<T, D, void_t<D<T>>> : std::true_type {};

template <typename T>
using has_m = decltype(std::declval<T>().m());

Finally, you can use that as

template<typename T>
struct Derived : public T
{
     auto m(std::true_type) { return T::m(); }
     auto m(std::false_type) { std::cout<<"Derived\n"; }
     auto m() { return m(detect</* const */ T, has_m>{}); }
                                ^^^^^^^^^^
                                //if m() is const
};

DEMO

Community
  • 1
  • 1
davidhigh
  • 14,652
  • 2
  • 44
  • 75
0

As Aslay Berby already said this is probably not the way that you would like to go. If you want to implement something like traits or policy-based design, the following code might be what you are looking for. In fact such designs are used quite commonly and have also idiomatic value.

#include <iostream>
using namespace std;

struct StandardTraits {void foo() {cout << "standard" << endl;}};

struct Traits1 {void foo() {cout << "traits1" << endl;}};
struct Traits2 {void foo() {cout << "traits2"<< endl;}};

template<typename T = StandardTraits>
class SomeClass
{
public:
    typedef T Traits;

    void useTraits() {traits.foo();}

private:
    Traits traits;
};

int main() {

    SomeClass<> x;
    SomeClass<Traits1> y;
    SomeClass<Traits2> z;

    x.useTraits();
    y.useTraits();
    z.useTraits();

    return 0;
}

// output:
// standard
// traits1
// traits2

See also: https://en.wikipedia.org/wiki/Policy-based_design

IceFire
  • 4,016
  • 2
  • 31
  • 51