C++模板与继承的比较

7

我意识到提问是多么困难...希望我能给出足够精确的示例来展示我的问题,并且足够简短以免混淆一切...至少还可以编辑。

所以这就是我的目前情况。当然,我在逻辑/结构上稍微改变了一些(无论如何,在命名方面也有所改变),试图集中于我的问题的本质:

// MyClass deals with lists (actually several data structures) of the
// type MyType which should support different types and has to be 
// efficiently dealt with. Templating seems just right here
class MyClass
{
  ...

  void doSomething<class MyType>(vector<MyType> someList);

  ...

  // At some point I have to extract elements of the type MyType.
  // The extractor obviously depends on MyType but it is not possible to
  // Create a general version that could use templates itself 
  // (unless I use a specialization for each possible MyType)

  // I am stuck between these two alternatives:

  // Possibility1:
  // Let the client pass the right extractor and template it.
  template<class Extractor, class MyType>
  void extract(const Extractor& extractor, const string& source, 
               vector<MyType>* dest)
  {
     extractor.extract(source, dest);
  }

  // Possibility2:
  // Use a member _extractor of some base type that has to be set
  // to a specialization. The ExtractorBase has a virtual method
  // template<T> void extract(const string& source, vector<T>* myType) = 0
  // with no definition that is only defined in subclasses wrt certain
  // postings.
  ExtractorBase _extractor;

  template<class MyType>
  void extract(const string& source, vector<MyType>* dest)
  {
     _extractor.extract(source, dest);
  }
}

目前我更倾向于选择可能性1,因为我不需要在提取器中处理所有MyType变体和相关提取器的继承问题,这也方便我在未来尝试其他变体。

另一方面,提取器可能需要复杂的代码和多个成员(例如将某些输入映射到特定值的大型映射表)。因此,使用模板不会带来性能收益。特别是仅使用头文件的提取器,甚至是预计要内联的函数对象,都不太可行。过去,这让我强烈感觉到,模板只会增加代码复杂性(必须处理实例化,使客户端代码更加困难等),因此我应该尽量避免使用它。

或者,还有第三种可能性我没有考虑吗?


“提取”具体指什么?只使用vector<MyType>吗? - vines
不清楚MyClass如何参与到提取过程中(以及为什么extract()是MyClass的一个方法?) - user396672
4个回答

2

最好选择第一种选项。它更加干净和易于维护。

因为从您的评论中,我得出了您对第二个选项做出了错误的假设:

// The ExtractorBase has a virtual method
// template<T> void extract(const string& source, vector<T>* myType) = 0;

不行。这是不可能的,template函数永远不能是virtual的。因此,要实现第二个选项,您必须选择一些肮脏且难以维护的方式,这不是一个好主意。


我认为Björn的意思是将_extractor成员变量设置为指针,并在调用模板函数之前实际指向适当的实现。这是可行的,但我不喜欢在可以使用模板的情况下使用虚拟分派:通常,在编译时解析得越多,就越好。无论如何,这似乎是一个经典的“试错”案例......从模板开始,如果它们引起了一些麻烦,就转向虚拟分派......API可以在任何一种方式下都基本相同,因此所涉及的工作应该是微不足道的。 - Tony Delroy

1

我认为第一种可能性更加灵活。

在第二种可能性中,我不认为将不必要的封装提取器作为类成员是有意义的。你还会在 MyClass 和 Extractor 之间增加更多的耦合,这并不是一个好事情。模板化可以减少耦合(在某种程度上),因此如果你有选择,这是一个更好的选择。


1

你有第三个选项,为 MyType 提供一个构造函数,它知道如何从 std::string 构造自己。或者更好的是一对迭代器,这样如果你需要从字符串构造一系列的 MyType,你可以使用范围。


首先,我必须从字符串中提取多个MyTypes,这样只留下迭代器。虽然这是一个很好的想法,但我应该补充说明,提取器取决于大量其他信息。最好的例子可能是将输入字符串映射到ID的“词汇表”。仍然非常感谢您的帮助。 - b.buchhold
@b.buchhold,我猜MyType的所有信息都是内部实现细节? - Nim

0

这似乎是一个策略模式的案例 -- 你的类有一个操作,其实现可能会有所不同。

以下是我在不同方法中看到的权衡。

模板解决方案将避免声明抽象接口类,并使用vtbl来确定要使用哪个实现。但它将强制您在编译时锁定应用程序。

继承解决方案将强制您声明抽象接口类,并承受在vtbl中查找实现的性能损失。但它将允许您在运行时选择提取实现。

不知道您的应用程序对性能有多重要,以及您需要多灵活,我可能会选择继承解决方案,因为我喜欢在抽象类中定义接口并编写代码的清晰度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接