【问题标题】:Can I use template member functions as an alternative to the CRTP in order to implement static polymorphism?我可以使用模板成员函数作为 CRTP 的替代方法来实现静态多态性吗?
【发布时间】:2021-10-05 10:27:35
【问题描述】:

我想在 C++ 中使用静态多态性来实现 template method 模式。

现在我有两个非常相似的课程AB。它们具有相同的公共 API 和完全相同的私有数据成员列表。大多数公共成员函数的实现也是相同的,但对于其中一些,这两个类的实现略有不同。

情况有点像这样:

class A {
public:
  /* Ctor etc... */
  int One() const;
  int Two() const;
  int Three() const; /* Calls PrivateA() internally */
private:
  int PrivateA() const;
  int a;
  int b;
  int c;
};

class B {
public:
  /* Ctor etc... */
  int One() const; /* Identical to A::One() */
  int Two() const; /* Identical to A::Two() */
  int Three() const; /* Same as in A::Three() but calls PrivateB() instead of PrivateA() internally */
private:
  int PrivateB() const; /* Specific for B */
  /* Identical data members (type and semantics) to A */
  int a;
  int b;
  int c;
};

我现在想通过将所有共享代码和数据成员移动到基类并从基类中委托给子类特定的成员函数来减少代码重复。也就是说,像这样:

class Base {
public:
  int One() const;
  int Two() const;
  int Three() const; /* Should delegate to Private() internally */
private:
  int a;
  int b;
  int c;
};

class A : public Base {
private:
  int Private() const;
};

class B : public Base {
private:
  int Private() const;
};

我知道我可以使用CRTP 以类似的方式解决这个问题:

template<typename T>
class Base {
public:
  int One() const;
  int Two() const;
  int Three() const; /* Should delegate to Private() internally */
private:
  int a;
  int b;
  int c;
};

class A : public Base<A> {
private:
  friend class Base<A>;
  int Private() const;
  int p;
};

class B : public Base<B> {
private:
  friend class Base<B>
  int Private() const;
  int q;
};

template<typename T>
int Base<T>::Three() const {
  return static_cast<const T*>(this)->Private();
}

int A::Private() const { return this->p; }
int B::Private() const { return this->q; }

int main() {
  A a{};
  B b{};

  /* Uses shared code from base class */
  a.One();
  b.One();

  /* Base class code statically dispatches to sub-class code via CRTP */
  a.Three(); /* Returns value of p */
  b.Three(); /* Returns value of q */

  return 0;
}

现在谈谈我的问题。

是否有可能使Base 成为模板类来实现相同的结果?我可以使用例如完成同样的事情吗?模板成员函数?

/* Note: no template */
class Base {
public:
  int One() const;
  int Two() const;

  template<typename T>
  int Three() const; /* Should delegate to Private() internally */
private:
  int a;
  int b;
  int c;
};

【问题讨论】:

  • 没有合理的界面。调用者必须提供 T,而且那是一个公共函数,所以其他代码需要知道如何调用它,这容易出错且繁琐。我只是将通用代码分解,并在派生类中实现不同的代码。
  • 所以没有办法将T“绑定”到例如A 这样用户就可以调用a.Three() 并且编译器计算出T 必须等于A,而不使用类模板和继承?
  • 也就是说,假设两个类之间的相似性不是偶然的,那么基类是合理的。通过耦合真正不相关的类,公共代码可能会成为问题。如果一个类需要更改并且通过使其可配置而影响其他类,则可能会引入错误。有时稍微重复一下是最好的,因此代码可以单独更改,而不会因为几个类而变得复杂,每个类明天都需要做自己的事情,但将它们全部纠缠在一个函数中。
  • 绑定它需要某种包装函数。也许您可以使用 std::function 或成员函数 ptr,并让派生类型传递要调用的函数,但更简单的是将 Three 放入派生类中。这样就可以将其绑定到正确的类型,而无需花哨和复杂的模板,并带来可疑的好处。

标签: c++ crtp static-polymorphism


【解决方案1】:

如果问题严格来说是“我可以” - 答案是“是的,你可以,非常简单”:

template<typename T>
int Three() const {
    return static_cast<const T*>(this)->Private();
}
    

但是,我不建议这样做,因为 CRTP 的好处之一是(几乎)不可能滥用演员表 - static_cast 几乎总是有效的。

使用上面显示的 sn-p,很容易出错并提供错误的模板参数,并使static_cast 未定义的行为。

结论 - 使用这种方法没有任何好处,但可以找到一个缺点。

【讨论】:

  • CRTP 案例的缺点是增加了编译时间和代码膨胀,这是因为所有“共享”成员函数为每个子类编译一次。这就是我想通过远离类模板解决方案来避免的。还是我在这里遗漏了什么?
  • @JonatanE 您应该进行试验(使用适当的优化级别)。链接时优化可能非常有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多