AlwaysLearning
AlwaysLearning

Reputation: 8051

Polymorphic function call without duplicate code

Suppose that all classes of a hierarchy implement a template member function g. All classes share the same implementation of two other functions f1 and f2 that call this template:

struct A {
    virtual void f1() {
        g(5);
    }
    virtual void f2() {
        g(5.5);
    }
private:
    template <typename T> void g(T) {std::cout << "In A" << std::endl;}
};

struct B: A {
    // Can I get rid of this duplicate code?
    virtual void f1() {
        g(5);
    }
    virtual void f2() {
        g(5.5);
    }
private:
    template <typename T> void g(T) {std::cout << "In B" << std::endl;}
};

struct C: A {
    // Can I get rid of this duplicate code?
    virtual void f1() {
        g(5);
    }
    virtual void f2() {
        g(5.5);
    }
private:
    template <typename T> void g(T) {std::cout << "In C" << std::endl;}
};

int main()
{
    B b;
    A &a = b;
    a.f1();
    return 0;
}

Since the implementations of f1 and f2 are identical in all the classes, how can I get rid of the duplicate code and still have the polymorphic call in main work as expected (i.e produce the output "In B")?

Upvotes: 8

Views: 187

Answers (2)

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145429

You can just factor out the class-specific things that the template function uses, such as (in your example) the class name:

#include <iostream>
using namespace std;

class A
{
private:
    virtual auto classname() const -> char const* { return "A"; }

protected:
    template <typename T> void g(T) {cout << "In " << classname() << endl;}

public:
    virtual void f1() { g(5); }
    virtual void f2() { g(5.5); }
};

class B
    : public A
{
private:
    auto classname() const -> char const* override { return "B"; }
};

class C
    : public A
{
private:
    auto classname() const -> char const* override { return "C"; }
};

auto main()
    -> int
{ static_cast<A&&>( B() ).f1(); }

Upvotes: 1

Note that the implementations of f1 and f2 in A, B, and C are not identical. Let's restrict it to f1s. One calls a function named ::A::g<int>, another one calls a function named ::B::g<int>, and the third one calls a function named ::C::g<int>. They are very far from identical.

The best you could do is have a CRTP-style base:

template <class Derived>
struct DelegateToG : public A
{
  void f1() override
  {
    static_cast<Derived*>(this)->g(5);
  }

  void f2() override
  {
    static_cast<Derived*>(this)->g(5.5);
  }
};

class B : public DelegateToG<B>
{
  friend DelegateToG<B>;
private:
  template <class T> void g(T) { /*...*/ }
};

class C : public DelegateToG<C>
{
  friend DelegateToG<C>;
private:
  template <class T> void g(T) { /*...*/ }
};

Upvotes: 3

Related Questions