简体   繁体   English

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

[英]Pass Templated iterator for STL Container

For an exercise for my C++ class (which hasn't covered Boost yet), I am having trouble writing a templated method to accept two iterators for summing numeric values in an STL container. 对于我的C ++类(尚未涵盖Boost)的练习,我在编写模板化方法以接受两个迭代器来汇总STL容器中的数值时遇到问题。
Consider the following example: 请考虑以下示例:

#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';
}

I expect this code to output 42 43 , but it fails to compile. 我希望这段代码输出42 43 ,但它无法编译。
The error g++ gives me is: 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> > >)’

If I comment out line 24, I get 42 as the output, as expected. 如果我注释掉第24行,我会得到42作为输出,正如预期的那样。
I get the same error message whether or not the second templated method is present or not, so for some reason, it's not able to resolve the call on line 24 to the second method I wrote. 无论第二个模板化方法是否存在,我都会得到相同的错误消息,因此由于某种原因,它无法将第24行的调用解析为我编写的第二个方法。

What signature must I have for the method that accepts two iterators? 对于接受两个迭代器的方法,我必须具有什么签名?


The reason why I'm stuck on this is because I need to support summing over the second element of std::map<K, V> . 我之所以坚持这一点,是因为我需要支持对std::map<K, V>的第二个元素进行求和。 This will require two more overloads to call ->second instead of dereferencing the iterator: 这将需要再调用两次->second而不是解除引用迭代器:
1. template<typename K, typename V> double Sum(const std::map<K, V> & m); 1. template<typename K, typename V> double Sum(const std::map<K, V> & m); (I'm okay with this one) (我对这个没问题)
2. and another one involving iterators over the map. 2.另一个涉及地图上的迭代器。

I feel like I'll be able to write the methods for std::map if I can figure out how to specify the passing of iterators for std::list and std::map . 我觉得如果我能弄清楚如何为std::liststd::map指定迭代器的传递,我将能够编写std::map的方法。 I'm okay with solutions that use template-templates. 我对使用模板模板的解决方案没问题。


EDIT: The precise wording of problem (omitting non-contributory sentences). 编辑:问题的准确措辞(省略非缴费句子)。
The containers from "the previous exercise" were std::vector<double> , std::list<double> , std::map<std::string, double> . 来自“上一个练习”的容器是std::vector<double>std::list<double>std::map<std::string, double>

Create a template function called Sum() that accepts the template argument T as input and returns a double. 创建一个名为Sum()的模板函数,它接受模板参数T作为输入并返回一个double。 The template argument will be a container. template参数将是一个容器。

  • In the implementation get an iterator (T::const_iterator) for the end. 在实现中为结束获取一个迭代器(T :: const_iterator)。 Then create a loop that iterates the container T and adds all values. 然后创建一个循环,迭代容器T并添加所有值。 Finally return the sum. 最后归还总和。
  • In the main program, call the Sum() function for the different container from the previous exercise. 在主程序中,为上一个练习中的不同容器调用Sum()函数。

The Sum() function created calculates the sum of the complete container. 创建的Sum()函数计算完整容器的总和。 Also create a Sum() function that calculates the sum between two iterators. 还要创建一个Sum()函数来计算两个迭代器之间的总和。 The function then uses the template argument for the iterator type and accepts two iterators, the start and end iterator. 然后该函数使用模板参数作为迭代器类型,并接受两个迭代器,即开始和结束迭代器。

You're overcomplicating this. 你这太复杂了。 You want a pair of iterators of any type? 你想要一对任何类型的迭代器? Well, that's just as simple as .. two arguments, of any type. 嗯,这就像两个参数一样简单,任何类型。

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

Problem solved. 问题解决了。

By the way, take a hint from the C++ Standard lib: If you can't de-reference the iterator, make the user provide a function to get the value from the iterator. 顺便说一下,从C ++标准库中获取提示:如果你不能取消引用迭代器,那么让用户提供一个函数来从迭代器中获取值。 Don't special-case std::map because tomorrow there's std::unordered_map and the day after that boost::multimap and all sorts of fun. 不要特殊情况std::map因为明天有std::unordered_mapboost::multimap之后的那一天以及各种各样的乐趣。 And what if I wanted you to sum the keys from the std::map , not the values? 而如果我想你总结从按键 std::map ,而不是价值?

Your hardcoded case is a little more complex. 您的硬编码案例有点复杂。 A pair of iterators that have to come from std::map ? 一对必须来自std::map的迭代器? Not even sure if possible without explicit template arguments. 如果没有明确的模板参数,甚至不确定。

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

Notice that I specifically said it had to be a std::map instantiation. 请注意,我特意说它必须是std::map实例化。 This allows the compiler to deduce the parameters. 这允许编译器推导出参数。 From here, you can access the iterators. 从这里,您可以访问迭代器。

As DeadMG said, the simple way is to template on the type of the iterator. 正如DeadMG所说,简单的方法是模板化迭代器的类型。 The common convention is, on the other hand, to pass iterators by value: 另一方面,通用约定是按值传递迭代器:

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

As to why the original code was not working, the problem is that the type of the container is not deducible: 至于为什么原始代码不起作用,问题是容器的类型是不可推导的:

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

When the compiler tries to infer the type of the arguments to Sum it only sees the type returned by v.begin() and v.end() , which are the iterator . 当编译器尝试推断Sum的参数类型时,它只能看到v.begin()v.end()返回的类型,它们是迭代器 From that type, it cannot guess the type of the container. 从该类型,它无法猜测容器的类型。 To be able to determine what the type T is, it would have to test all non template types, and generate all infinite possible instantiations of template types to look whether they have a nested type const_iterator that matches the type of v.begin() and v.end() . 为了能够确定类型T是什么,它必须测试所有非模板类型,并生成模板类型的所有无限可能实例化,以查看它们是否具有与v.begin()的类型匹配的嵌套类型const_iterator v.end() Because that would be impossible, to achieve, the language forbids it in the first place. 因为那是不可能实现的,语言首先禁止它。

Beyond that, and related to the comment [*] , even if the type would be deducible, overload resolution is performed on the arguments to the function, and not how the expression is later use. 除此之外,与注释[*]相关 ,即使类型可以推断,也会对函数的参数执行重载解析,而不是以后如何使用表达式。 In your program, the argument to .begin() is a std::vector<double> non-const lvalue . 在你的程序中, .begin()的参数是std::vector<double>非const lvalue Because it is not const, the overload selected will yield a non-const iterator (even if in the function you want to call, there is no need to read it). 因为它不是const,所选择的重载将产生一个非const迭代器(即使在你想要调用的函数中,也不需要读取它)。

The distinguishing feature when contrasting iterators from eg std::list with iterators from std::map is that the latter have a pair type as their value_type . std::list中的迭代器与std::map迭代器进行对比时的区别特征是后者具有一对类型作为其value_type That is to say, given std::map<K, V> then both std::map<K, V>::value_type and std::iterator_traits<std::map<K, V>::iterator>::value_type are std::pair<const K, V> . 也就是说,给定std::map<K, V>然后std::map<K, V>::value_typestd::iterator_traits<std::map<K, V>::iterator>::value_typestd::pair<const K, V>

Hence I suggest your Sum template accept any iterator, but that it operates not on elements given from the iterator (ie *it ) and instead on a 'view': element(*it) . 因此,我建议您的Sum模板接受任何迭代器,但它不对迭代器(即*it )给出的元素进行操作,而是对'view': element(*it) Now you can take care to make sure that element 'does the right thing' when faced with a pair. 现在你可以注意确保element在面对一对时“做正确的事”。

As a hint, you could declare Sum as the following (with a bit of metaprogramming for getting the return type correctly): 作为提示,您可以将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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM