5

对于我的 C++ 类(尚未涵盖 Boost)的练习,我无法编写模板化方法来接受两个迭代器来对 STL 容器中的数值求和。
考虑以下示例:

#include <iostream>
#include <iterator>
#include <vector>

template<typename T>
double Sum(const T & c) {
    return 42.0;    // implementation stubbed
}

// need help writing this method signature to accept two iterators
template<typename T>
double Sum(const typename T::const_iterator & begin,
           const typename T::const_iterator & end) {
    return 43.0;    // another implementation stub
}

int main() {
    std::vector<double> v;
    v.push_back(3.14);
    v.push_back(2.71);
    v.push_back(1.61);    // sums to 7.46

    std::cout << Sum(v) << ' '              // line 23
              << Sum(v.begin(), v.end())    // line 24
              << '\n';
}

我希望此代码能够输出42 43,但无法编译。
g++ 给我的错误是:

test_exercise2.cpp: In function ‘int main()’:
test_exercise2.cpp:24: error: no matching function for call to ‘Sum(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >)’

如果我注释掉第 24 行,我会得到42预期的输出。
无论是否存在第二个模板化方法,我都会收到相同的错误消息,因此由于某种原因,它无法将第 24 行的调用解析为我编写的第二种方法。

对于接受两个迭代器的方法,我必须有什么签名?


我之所以坚持这一点是因为我需要支持对std::map<K, V>. 这将需要另外两个重载来调用->second而不是取消引用迭代器:
1. template<typename K, typename V> double Sum(const std::map<K, V> & m);(我可以接受这个)
2.另一个涉及地图上的迭代器。

std::map如果我能弄清楚如何为std::listand指定迭代器的传递,我觉得我将能够编写方法std::map。我可以接受使用模板模板的解决方案。


编辑:问题的准确措辞(省略非贡献的句子)。
“上一个练习”中的容器是std::vector<double>, std::list<double>, std::map<std::string, double>.

创建一个名为 Sum() 的模板函数,它接受模板参数 T 作为输入并返回一个双精度值。模板参数将是一个容器。

  • 在实现中,最后得到一个迭代器(T::const_iterator)。然后创建一个循环来迭代容器 T 并添加所有值。最后返回总和。
  • 在主程序中,为与上一个练习不同的容器调用 Sum() 函数。

创建的 Sum() 函数计算整个容器的总和。还要创建一个 Sum() 函数来计算两个迭代器之间的总和。然后该函数使用迭代器类型的模板参数并接受两个迭代器,即开始迭代器和结束迭代器。

4

3 回答 3

7

你把这件事复杂化了。你想要一对任何类型的迭代器吗?好吧,这就像......任何类型的两个参数一样简单。

template<typename Iterator>
double Sum(const Iterator& begin,
           const Iterator& end) {
    return 43.0;    // another implementation stub
}

问题解决了。

顺便说一句,从 C++ 标准库中得到一个提示:如果您无法取消引用迭代器,请让用户提供一个函数来从迭代器中获取值。不要特殊情况std::map,因为明天还有后天,还有std::unordered_map各种各样boost::multimap的乐趣。如果我想让你对 中的std::map不是值求和怎么办?

您的硬编码案例有点复杂。一对必须来自的迭代器std::map?如果没有明确的模板参数,甚至不确定是否可能。

template<typename K, typename V, typename Comp, typename Alloc>
double Sum(
    const std::map<K, V, Comp, Alloc>& map
) { ... }

请注意,我特别说过它必须是std::map实例化。这允许编译器推断参数。从这里,您可以访问迭代器。

于 2012-08-31T03:47:02.473 回答
6

正如 DeadMG 所说,简单的方法是在迭代器的类型上进行模板化。另一方面,常见的约定是按值传递迭代器:

template <typename Iterator>
double Sum( Iterator begin, Iterator end );

至于为什么原始代码不起作用,问题是容器的类型不可推断:

template <typename T>
double Sum( T::const_iterator begin, T::const_iterator end );
Sum( v.begin(), v.end() );        // [*] Assume v == const std::vector<double>&

当编译器试图推断参数的类型时,Sum它只会看到 and 返回的类型v.begin()v.end()它们是迭代器。从该类型中,它无法猜测容器的类型。为了能够确定类型T是什么,它必须测试所有非模板类型,并生成模板类型的所有无限可能实例,以查看它们是否具有与andconst_iterator的类型匹配的嵌套类型。因为那是不可能的,所以语言一开始就禁止它。v.begin()v.end()

除此之外,与注释[*]相关,即使类型是可推导的,重载决议也会在函数的参数上执行,而不是表达式稍后如何使用。在您的程序中, to 的参数.begin()是一个std::vector<double>非常量左值。因为它不是 const,所以选择的重载会产生一个非常量迭代器(即使在你要调用的函数中,也不需要读取它)。

于 2012-08-31T03:58:46.463 回答
5

std::list将迭代器 from eg与 iterators from进行对比时的显着特征std::map是后者有一个 pair 类型作为它们的value_type. 也就是说,给定std::map<K, V>thenstd::map<K, V>::value_typestd::iterator_traits<std::map<K, V>::iterator>::value_typeare std::pair<const K, V>

因此,我建议您的Sum模板接受任何迭代器,但它不在迭代器(即*it)给出的元素上运行,而是在 'view': 上运行element(*it)。现在,您可以注意确保element在面对一对时“做正确的事”。

作为提示,您可以声明Sum如下(使用一些元编程来正确获取返回类型):

namespace result_of {

// Second template parameter is an implementation detail
template<
    typename Iterator
    , typename ValueType = typename std::iterator_traits<Iterator>::value_type
>
struct Sum {
    // general case: sum over the value type directly
    typedef ValueType type;
};

// If an iterator admits an std::pair as its value_type then we end up here
template<typename Iterator, typename Key, typename Value>
struct Sum<Iterator, std::pair<Key, Value> > {
    // special case: sum over the second type of the value
    typedef Value type;
};

} // result_of

template<typename Iterator>
typename result_of::Sum<Iterator>::type Sum(Iterator begin, Iterator end);
于 2012-08-31T04:30:40.163 回答