为STL容器传递模板化迭代器

Pra*_*mar 5 c++ templates iterator stl

对于我的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';
}
Run Code Online (Sandbox Code Playgroud)

我希望输出这个代码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> > >)’
Run Code Online (Sandbox Code Playgroud)

如果我注释掉第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::liststd::map.我对使用模板模板的解决方案没问题.


编辑:问题的准确措辞(省略非缴费句子).
从"前面的练习"的容器是std::vector<double>,std::list<double>,std::map<std::string, double>.

创建一个名为Sum()的模板函数,它接受模板参数T作为输入并返回一个double.template参数将是一个容器.

  • 在实现中为结束获取一个迭代器(T :: const_iterator).然后创建一个循环,迭代容器T并添加所有值.最后归还总和.
  • 在主程序中,为上一个练习中的不同容器调用Sum()函数.

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

Pup*_*ppy 7

你这太复杂了.你想要一对任何类型的迭代器?嗯,这就像两个参数一样简单,任何类型.

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

问题解决了.

顺便说一下,从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
) { ... }
Run Code Online (Sandbox Code Playgroud)

请注意,我特意说它必须是一个std::map实例.这允许编译器推导出参数.从这里,您可以访问迭代器.


Dav*_*eas 6

正如DeadMG所说,简单的方法是模板化迭代器的类型.另一方面,通用约定是按值传递迭代器:

template <typename Iterator>
double Sum( Iterator begin, Iterator end );
Run Code Online (Sandbox Code Playgroud)

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

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

当编译器试图推断的参数类型Sum,只看到了由返回的类型v.begin()v.end(),这是迭代器.从该类型,它无法猜测容器的类型.为了能够确定什么类型T的,那就要测试所有非模板类型,并生成模板类型的所有无限可能的实例看他们是否有一个嵌套的类型const_iterator匹配的类型v.begin()v.end().因为那是不可能实现的,语言首先禁止它.

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


Luc*_*ton 5

将迭代器 from egstd::list与迭代器 from 进行对比时的区别特征std::map是,后者有一个 pair 类型作为它们的value_type。也就是说,考虑到std::map<K, V>那么这两个std::map<K, V>::value_typestd::iterator_traits<std::map<K, V>::iterator>::value_typestd::pair<const K, V>

因此,我建议你的Sum模板接受任何迭代器,但它不工作从迭代器(即给定的元素*it),而是在“视图”: 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);
Run Code Online (Sandbox Code Playgroud)