【问题标题】:Is it bad practice to use an abstract base class to enforce a common interface for a template parameter type?使用抽象基类为模板参数类型强制使用公共接口是不好的做法吗?
【发布时间】:2021-09-13 16:09:42
【问题描述】:

我是模板编程的新手,偶然发现了这个习语。例如,类似:

class FooBase {
 public:
  virtual void do_something() = 0;
};

template <class Foo> // Foo is derived from FooBase
void g(Foo& foo) {
  static_assert(std::is_base_of<FooBase, Foo>::value);
  // ...
  foo.do_something();
  // ...
}

在我自己看来,这种模式很有用,因为:

  1. 纯虚函数声明具有显式签名;与 C++20 概念 requires 子句(如果有的话)相比,它很容易指定属性、参数和返回类型。
  2. 这是一个很好的文档;只需阅读 FooBase 标头,就可以清楚地了解定义新 Foo 类的要求。
  3. 可以将所有 Foo 的通用功能重构为同一个 FooBase 类。

但是,我担心使用 virtual 函数对性能的影响。我的理解是运行时没有成本,因为函数是从派生类调用的;但链接器将无法内联函数。

另一个缺点是不允许使用虚拟模板函数。

最后,我担心这可能是一种代码异味,因为它使用运行时多态的特性来执行静态检查。 C++20 概念可能是执行此操作的“正确”方法,但由于上述三个原因,它们似乎不太方便。

【问题讨论】:

  • 具体执行在哪里?任何具有 do_something 成员且在调用中不接受任何参数的类都将受到您的函数模板的欢迎。
  • 在这种情况下,void g(FooBase&amp; foo) { ... }不会做你想做的事吗?
  • @StoryTeller-UnslanderMonica 是的,我应该包含一个 static_assert,但在示例中省略了它
  • @TedLyngmo 但是 Foo 的类型在编译时是已知的,所以我不需要使用运行时多态性
  • @NickFP 在您编辑后,我的(现已删除)评论代表您的 “运行时无需成本,因为该函数是从派生类调用的” 不是可靠的陈述,因为任何FooDerived&amp; 都可能是从FooDerived派生的类型。

标签: c++ templates polymorphism c++-concepts


【解决方案1】:

这是一种不好的做法,因为您使用了错误的工具来完成这项工作。您正在编写的代码传达了错误的信息,并且没有实际的执行机制。

动态多态性的关键在于它是动态的:在运行时定义,这样您就可以将对象传递给在编译时不完全知道它们的类型的函数。这允许针对基类编写一个位置的代码,而不是将其导出到标头等中。并且在任何时候使用的实际类也不能导出到标题等中。只有类的来源需要知道实际的类型。

像模板这样的编译时多态性是基于在编译时知道一切的。所有代码,包括源代码和目标代码,都需要知道类型是什么。

本质上,与编译时多态性相比,您喜欢动态多态性阐明其要求的方式,但您试图通过使用编译时多态性来回避它的性能成本。这会造成代码混乱,因为您会混淆机制。

如果有人看到带有virtual 函数的基类,他们会假设您的代码将传递指向实际类的指针/引用。这是在大多数情况下使用它们的期望的一部分(即使基于virtual 的类型擦除也能有效地做到这一点)。看到一堆按值获取类型的模板函数会令人困惑。

此外,您没有强制执行机制。采用FooBase&amp; 的函数确保用户不能使用不是实际FooBase 派生类型的类型调用它(好吧,您可以将其隐式转换为1,但我们在这里忽略不忠)。您的模板函数,避开概念,没有类似的强制执行。您可以证明它必须是 FooBase 派生类型,但您不会静态强制它。

至少,模板参数应该声明为std::derived_from&lt;FooBase&gt; Foo(不,static_assert 不是一个好主意)。

但实际上,您应该使用正确的概念。您需要编译时多态性,无论您对概念的个人感受是什么,概念都是 C++20 用于定义编译时多态性原型的语言机制。如果您使用概念,任何人都不会对代码的含义和意图感到困惑。

【讨论】:

    猜你喜欢
    • 2011-07-14
    • 2016-09-20
    • 1970-01-01
    • 2017-11-27
    • 2010-11-18
    • 2011-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多