Search code examples
c++classtemplatesmember-functionsclass-template

Define member functions once for two similar classes?


Is it possible to avoid defining the same functions for structs A, B below twice? Their members are named exactly the same, but the v0, v1 ... vN members are of different type between the two structs A and B. If it helps the members v* are all derived from the same struct V.

Non-trivial functions (i.e. assignment =) can reuse a templated copy function outside the structs, like below, but it'd be preferred/cleaner if this was defined once and within the struct.

Is there a clean way to templatize A and B definition into a single one?

template <class T1, class T2>
void copy(T1& to, T2& from)
{
    to.v0 = from.v0;
    to.v1 = from.v1;
    to.type = from.type;
}

enum class E { TYPE_0, TYPE_1 };

struct B;

struct A 
{
    C0<float> v0;
    C1<int> v1;
    E type;

    A& operator = (const B& t)
    {
        copy(*this, t);
        return *this;
    } 
    
    string strType() { return string(type); }    
};

struct B
{
    D0<float> v0;
    D1<int> v1;
    E type;

    B& operator = (const A& t)
    {
        copy(*this, t);
        return *this;
    } 

    string strType() { return string(type); }
}

Solution

  • You can keep a common base template class for the common functionalities and inherit from it for class A and B.

    However, the copy assignment operator is a special member function, which I do not think one could template for different types return in the base class. Therefore you need to provide for each class.

    Meaning, you can do

    enum class E { TYPE_0, TYPE_1 };
    struct B;
    
    template<typename ClassA, typename ClassB> struct Common
    {
       template<E type> 
       std::string strType() { return std::to_string(static_cast<int>(type)); }
       // need to cast the type to int before you convert std::to_string
    
       // other common-member-functions
    };
    
    struct A : public Common<A, B>
    {
       C0 v0;
       C1 v1;
       E type;
       // ... A& operator = (const B& t)
       // bring the common functionalities to A
       using Common<A, B>::strType;
       // ... other member-functions
    };
    
    struct B : public Common<B, A>
    {
       D0 v0;
       D1 v1;
       E type;
       // ... B& operator = (const A& t)
       // bring the common functionalities to A
       using Common<B, A>::strType;
       // ... other member-functions
    };
    

    However, both structs A, B, seems to only differ from the two members (i.e. C0, C1 and D0, D1 respectively), combining both classes to one by making a class-template, also an alternative:

    Following is an example code:

    #include <iostream>
    #include <vector>
    #include <string>
    
    enum class E { TYPE_0, TYPE_1 };
    
    template<typename T1,  typename T2>
    struct AandB
    {
       T1 v0;
       T2 v1;
       E type;
       AandB() : type{ E::TYPE_0 } {}
       AandB& operator= (const AandB& rhs) // one operator =
       {
          v0 = rhs.v0;
          v1 = rhs.v1;
          type = rhs.type;
          return *this;
       }
    
       std::string strType() const { return std::to_string(static_cast<int>(type)); }
    };
    
    int main()
    {
       using C0 = std::vector<float>;
       using C1 = std::vector<int>;
       AandB<C0, C1> obj;
       std::cout << obj.strType() ; // Prints: 0
    }