对容器进行操作的方法:硬编码容器类型,还是使用通用模板迭代器?

时间:2015-01-22 15:27:00

标签: c++ templates api-design

我有代码,从概念上讲,我的输入是Foo个对象的容器。代码逐个“处理”这些对象,所需的结果是填充FooProduct个结果对象的容器。

我只需要一次通过输入容器。 “处理”是有状态的(这不是std::transform()),结果对象的数量与输入对象的数量无关。

在这里,我可以看到两种明显的方法来定义API。

最简单的方法是对特定类型的容器进行硬编码。例如,我可以决定我期待vector个参数,例如:

void ProcessContainerOfFoos(const std::vector<Foo>& in, std::vector<FooProduct>&out);

但是,我没有任何理由将客户端代码限制为特定类型的容器。我没有将参数类型专门限制为vector,而是将方法设为通用,并使用迭代器作为模板参数:

/**
 * @tparam Foo_InputIterator_T An input iterator giving objects of type Foo.
 * @tparam FooProduct_OutputIterator_T An output iterator writing objects 
 *                                     of type FooProduct.
 */
template<typename Foo_InputIterator_T, typename FooProduct_OutputIterator_T >
void ProcessContainerOfFoos(Foo_InputIterator_T first, Foo_InputIterator_T last,
                     FooProduct_OutputIterator_T out);

我在这两种配方之间进行辩论。

考虑

对我来说,第一个代码在我看来是“更容易”而第二个代码似乎“更正确”:

  • 非模板类型使签名更清晰;我不需要在文档中解释使用什么类型以及模板参数的约束条件。
  • 没有模板我可以隐藏.cpp文件中的实现;使用模板我需要在头文件中公开实现,强制客户端代码包含实际处理逻辑所需的任何内容。
  • 模板化的版本感觉它更清楚地表达了我的意图,因为我宁愿对使用的容器类型漠不关心。
  • 模板化版本更灵活,更可测试 - 例如,在我的代码中,我可能正在使用一些自定义数据结构MySuperEfficientVector,但我仍然可以测试MyFooProcessor而不依赖于自定义类。

除了主观选择,考虑到这些因素,是否有一个主要原因选择其中一个?同样,是否有更好的方法来构建这个我缺少的API吗

2 个答案:

答案 0 :(得分:2)

除了您已经列出的注意事项:

  • 模板版本允许客户端代码传递任何迭代器 范围,例如子范围或反向迭代器,而不仅仅是从开始到结束的整个容器。
  • 模板版本允许传递Foo以外的值类型。为了使其有用,处理必须是通用的。
  • 如果模板仅使用特定的值类型,并且用户尝试将迭代器用于错误的类型,则错误消息可能无法描述其错误。如果这是一个问题,您可以使用类型特征为用户提供更好的错误:static_assert(std::is_same<Iter::value_type, Foo>::value, "I want my Foo");在将概念提议添加到标准之前,没有好的方法将签名中的模板类型的要求传达给用户。

还可以选择提供这两种功能。硬编码的可以委托给模板版本。这给你带来了两个版本的优势,但代价是膨胀你的api。

答案 1 :(得分:1)

这取决于。如果这个函数将用于向量的时间,为什么还要麻烦?

我建议只在必要时才进行模板化版本。提前预测这些事情很难。