Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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")?

like image 316
AlwaysLearning Avatar asked Mar 25 '16 12:03

AlwaysLearning


2 Answers

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) { /*...*/ }
};
like image 53
Angew is no longer proud of SO Avatar answered Nov 12 '22 21:11

Angew is no longer proud of SO


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(); }
like image 44
Cheers and hth. - Alf Avatar answered Nov 12 '22 20:11

Cheers and hth. - Alf