【问题标题】:Define member functions once for two similar classes?为两个相似的类定义一次成员函数?
【发布时间】:2020-12-15 23:27:06
【问题描述】:

是否可以避免为结构 A, B​​strong> 定义相同的函数两次?它们的成员名称完全相同,但 v0, v1 ... vN 成员在两个结构 AB 之间的类型不同.如果有帮助,成员 v* 都来自同一个结构 V

非平凡函数(即赋值 =)可以在结构外部重用模板化复制函数,如下所示,但如果在结构内定义一次,它会是首选/更清洁。

有没有一种简洁的方法可以将 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); }
}

【问题讨论】:

    标签: c++ class templates member-functions class-template


    【解决方案1】:

    您可以为常用功能保留一个common 基模板类,并为类AB 继承它。

    但是,复制赋值运算符是一个特殊的成员函数,我认为不能为基类中的不同类型返回模板。因此,您需要为每个班级提供。

    意思是,你可以做

    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,也是一个替代方案:

    以下是示例代码:

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

    【讨论】:

      【解决方案2】:

      这正是称为 CRTP(Curiously Recurring Template Pattern)的模式的用例。

      这个想法是定义一个基类模板,将派生类作为模板参数。然后就可以安全地将基类中的this 强制转换为派生类,并访问基类中的派生类成员。

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

      #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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-12
        • 2015-05-01
        • 2012-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-11
        • 1970-01-01
        相关资源
        最近更新 更多