mkmostafa
mkmostafa

Reputation: 3171

how to avoid code duplication between syntactically identical const and non const functions that are not semantically identical

#include <iostream>
using namespace std;

class A 
{
    public:
    A() : x(0) {}
    // notice: not identical to const version but does update
    void FA() {std::cout << "A" << std::endl; x++;}
    void FA() const {std::cout << "const A" << std::endl;}
    private:
    int x;
};

class B
{
    public:
    B() : x(0) {}
    // notice: not identical to const version but does update
    void FB() {std::cout << "B" << std::endl; x++;}
    void FB() const {std::cout << "const B" << std::endl;}
    private:
    int x;
};

class C
{
    public:
    void FC()
    {
        bool condition = true; // should be set to a real condition

        if(condition)
        {
            a.FA();
        }
        b.FB();
    }

    void FC() const
    {
        bool condition = true; // should be set to a real condition

        if(condition)
        {
            a.FA();
        }
        b.FB();
    }

    private:
    A a;
    B b;
};

int main() {
    C c;
    c.FC();

    const C cc;
    cc.FC();

    return 0;
}

First, sorry for the lengthy title. How to avoid code duplication in class C in functions FC, FC const? given that you can not use the trick of casting this to const and calling the const FC version from the non const FC version because the non const FC's body actually will call functions that will do updates an are not identical to their corresponding constants.

Upvotes: 5

Views: 141

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69902

Great answer by Ralph. I think it's worth mentioning that providing free functions with the same name, but with different argument types, we allow ourselves to express intent at a high level, while the free functions act as adapters. We allow ADL to locate the correct function overload (or specialisation) for us.

In this way, we need not be required to know the names of member functions in order to express logic. In this example, it would make class C more amenable to modification and evolution later on:

#include <iostream>
#include <utility>
using namespace std;

// general case
template<class T> 
  decltype(auto) apply_F(T&& t)
{
  return t.F();
}

// call apply_F with either an X or a Y and return the result )can be void)   
template<class X, class Y>
  auto conditionally_apply_F(bool condition, X&& x, Y&& y)
  -> std::common_type_t<decltype(apply_F(x)), decltype(apply_F(y))>
{
  if (condition) {
    return apply_F(x);
  }
  else {
    return apply_F(y);
  }
}

// provides F member function - no need for specific adapter
class A 
{
    public:
    A() : x(0) {}
    // notice: not identical to const version but does update
    void F() {std::cout << "A" << std::endl; x++;}
    void F() const {std::cout << "const A" << std::endl;}
    private:
    int x;
};


// has differing names implementing the concept of F - so we'll need an adapter
class B
{
    public:
    B() : x(0) {}
    // notice: not identical to const version but does update
    void F_mutable() {std::cout << "B" << std::endl; x++;}
    void F_const() const {std::cout << "const B" << std::endl;}
    private:
    int x;
};

// adapter overloads
decltype(auto) apply_F(B const& t)
{
  return t.F_const();
}

decltype(auto) apply_F(B& t)
{
  return t.F_mutable();
}

// class C current expressed in terms of A and B, but no longer tied to these types    
class C
{
    public:
    void FC()
    {
        bool condition = true; // should be set to a real condition
        conditionally_apply_F(condition, a, b);
    }

    void FC() const
    {
        bool condition = true; // should be set to a real condition
       conditionally_apply_F(condition, a, b);
    }

    private:
    A a;
    B b;
};

int main() {
    C c;
    c.FC();

    const C cc;
    cc.FC();

    return 0;
}

Upvotes: 0

Ralph Tandetzky
Ralph Tandetzky

Reputation: 23640

Let a template member function do the actual work. In other words: Try this:

class C
{
public:
    void FC()
    {
        FC_Impl( *this );
    }

    void FC() const
    {
        FC_Impl( *this );
    }

private:
    template <typename Self>
    static void FC_Impl( Self & self )
    {
      bool condition = true; // should be set to a real condition

      if(condition)
      {
          self.a.FA();
      }
      self.b.FB();
    }

    A a;
    B b;
};

Upvotes: 7

Related Questions