为两个相似的类定义一次成员函数?

Define member functions once for two similar classes?

是否可以避免为下面的结构A、B定义相同的函数两次?它们的成员命名完全相同,但是 v0, v1 ... vN 成员在两个结构 AB。如果它有助于成员 v* 都派生自相同的结构 V.

非平凡函数(即赋值 =)可以在结构外重用模板化复制函数,如下所示,但如果它在结构内定义一次,则为 preferred/cleaner。

有没有一种干净的方法可以将 AB 定义模板化为一个定义?

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); }
}

您可以为常用功能保留一个 common 基本模板 class,并从中继承 class AB

然而,复制赋值运算符是一个特殊的成员函数,我不认为可以在基 class 中为不同类型 return 模板。因此,您需要为每个 class.

提供

意思是,你可以做到

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
};

但是,AB 这两个结构似乎只与两个成员不同(即 C0C1D0D1),通过制作 class-template 将两个 class 合并为一个,也可以选择:

示例代码如下:

#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
}

这正是称为 CRTP(奇怪的重复模板模式)的模式的用例。

我们的想法是定义一个基础 class 模板,它将派生的 class 作为模板参数。然后您可以安全地将基 class 中的 this 转换为派生 class 并访问基 class.[=13= 中的派生 class 成员]

这也适用于复制赋值运算符。

#include <string>
#include <iostream>

template<typename Derived>
struct CRTPBase {
    
    Derived& self() { return static_cast<Derived&>(*this); }
    const Derived& self() const { return static_cast<const Derived&>(*this); }

    std::string strType() { return self().type;}

    template<class OtherDerived>
    Derived& operator=(const CRTPBase<OtherDerived>& other) {
        self().type = other.self().type;
        return self();
    }
};

struct A : public CRTPBase<A>
{
   using Base = CRTPBase<A>;
   std::string type = "A";

   using Base::operator=;
};

struct B :  public CRTPBase<B>
{
      using Base = CRTPBase<B>;
   std::string type = "B";

   using Base::operator=;
};

int main() {

    A a;
    B b;
    std::cout << a.strType() << std::endl; // Prints: A
    std::cout << b.strType() << std::endl; // Prints: B
    
    a = b;
    
    std::cout << a.strType() << std::endl; // Now prints: B
}

实例here.