我如何编写一个可以以类型安全的方式迭代泛型类型的函数

时间:2015-12-15 23:44:57

标签: c++ c++11 generics types

我想编写一个函数,它可以以类型安全的方式在给定的泛型类型上输入迭代器。一个可能的用例是编写像accumulate / map / fold这样的函数:

#include <iterator>
#include <vector>
#include <functional>

template <typename V, typename K>
K accumulate(
      std::function<K(K, V)> accumulator,
      /* WHAT TYPE DO I PUT HERE */ it,
      /* WHAT TYPE DO I PUT HERE */ end,
      K initial) {
  K sum = initial;
  for (; it != end; ++it) {
    V item = *it;
    sum = accumulator(sum, item);
  }
  return sum;
}

如何以编译器检查类型和所有好东西的方式执行此操作?

之前曾问过here

2 个答案:

答案 0 :(得分:0)

研究标准库容器中使用的模式。问题是你试图明确容器应该定义的许多类型。此外,谨慎使用auto意味着您根本没有明确声明某些类型。

template <typename C, typename R>
R accumulate(
      std::function<R(R, C::value_type)> accumulator,
      R initial
) {
  auto sum = initial;
  for (auto it = C.begin(); it != C.end(); ++it) {
    sum = accumulator(sum, *it);
  }
  return sum;
}

通过使用容器类型作为模板参数,您可以依赖其value_type定义来确定累加器函数的类型安全性。通过将auto用于迭代器并使用标准begin()end()方法,您甚至不必明确提及其类型。 (只是为了确定,如果你需要它们,它们是C::iterator。)

答案 1 :(得分:0)

我想我理解你的问题,我可以想到两种不同的方法,两者都有利有弊:

[1]对迭代器使用第三个模板类型,比如ITER。优点是更简单的代码,适用于更多的集合类型,更少的限制。缺点是,不捕获ITER和V之间的依赖性约束,即ITER必须是迭代类型V的迭代器,即V = ITER :: value_type。

[2]显式调出依赖类型而不是创建新的模板参数。它更准确地捕获依赖类型,减少模板参数的数量。缺点是,它依赖于集合的类型声明,这可能不是标准(如果ITER没有子类型ITER :: value_type,或者命名不同?)。您将在此处使用kewyword typename作为依赖类型。 在这里,编译器可以更好地处理编译错误,但请注意,除非您实际实例化它,否则很难获得有关类型错误的任何反馈。因此,您需要使用2/3具体类型来测试代码。

C ++代码说明了这两种方法。 BTW你为什么使用typename,我认为它应该只是“template&lt; class V,..”。 typename用于依赖类型(例如函数accumulate2)

template <class V, class K, class ITER>
K accumulate1(
      std::function<K(K, V)> accumulator,
      ITER it,
      ITER end,
      K initial) {
  K sum = initial;
  for (; it != end; ++it) {
    V item = *it;
    sum = accumulator(sum, item);
  }
  return sum;
}

template <class K, class ITER>
K accumulate2(
      std::function<K(K, typename ITER::value_type)> accumulator,
      ITER it,
      ITER end,
      K initial) {
  K sum = initial;
  for (; it != end; ++it) {
    typename ITER::value_type item = *it;
    sum = accumulator(sum, item);
  }
  return sum;
}

string AppendInt(const string& s, int n) {
  char buffer [65];
  sprintf(buffer, "%s/%d", s.c_str(), n);
  return string(buffer);
}

int main(int argc, char* argv[]) {
  std::function<string(string,int)> fun =
      [](string s, int n) -> string { return AppendInt(s,n); };
  string initial = "x";
  vector<int> array = {13, 24, 50, 64, 32};
  string sum1 = accumulate1(fun, array.begin(), array.end(), initial);
  string sum2 = accumulate2(fun, array.begin(), array.end(), initial);
  printf("accumulate1 : %s\n", sum1.c_str()); // o/p: x/13/24/50/64/32
  printf("accumulate2 : %s\n", sum2.c_str()); // o/p: x/13/24/50/64/32
}