简体   繁体   English

为什么优先队列的迭代器类型与范围构造函数错误?

[英]Why iterator type of priority_queue is wrong with range constructor?

The code is as following.代码如下。

#include <iostream>
#include <queue>
#include <unordered_map>

using namespace std;

typedef unordered_map<int,int>::iterator myIt;

class cmpHelper {
    public:
    bool operator()(myIt l, myIt r){return l->second > r->second;}
};

int main(int argc, char* argv[]){
    unordered_map<int,int> freq_cnt({{3,1},{2,4},{5,2}});

    priority_queue<myIt,vector<myIt>, cmpHelper> h(freq_cnt.begin(), freq_cnt.end());
    //priority_queue<myIt, vector<myIt>, cmpHelper> h;
}

and hereunder shows corresponding compiler info并在下面显示相应的编译器信息

 g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Default constructor is OK.默认构造函数是可以的。 And I tried to go through code under directory /usr/include/c++/7/bits/ , but still no any idea.我试图通过目录/usr/include/c++/7/bits/下的代码,但仍然没有任何想法。 Please help point out what the problem is.请帮忙指出是什么问题。 Thanks.谢谢。

It's because the constructor that takes iterators dereferences the iterators to populate the container.这是因为采用迭代器的构造函数取消引用迭代器以填充容器。

Put the actual iterators in the container instead:将实际的迭代器放在容器中:

std::priority_queue<myIt, vector<myIt>, cmpHelper> h;

for(auto it = freq_cnt.begin(); it != freq_cnt.end(); ++it) {
    h.push(it);
}

What you are trying to do would be similar to this:您正在尝试做的将与此类似:

std::priority_queue<myIt, vector<myIt>, cmpHelper> h;

for(auto it = freq_cnt.begin(); it != freq_cnt.end(); ++it) {
    h.push(*it);  // <- NOTE: dereferencing the iterator
}

To clarify further.进一步澄清。 Check this simplified example:检查这个简化的例子:

#include <iostream>
#include <vector>

int main() {
    using myIt = std::vector<int>::iterator;

    std::vector<int> ints{1, 2, 3};

    myIt beg = ints.begin(); // beg is a `myIt` and *beg is an `int&`
    myIt end = ints.end();

    //std::vector<myIt> iterators(beg, end); // NOK: *beg is NOT a `myIt`, but an `int&`
    std::vector<int> ints_copy(beg, end);    // OK: *beg is an `int&`
}

You use iterators to populate containers by dereferencing ( *beg above) the iterators.您可以通过取消引用(上面的*beg )迭代器来使用迭代器来填充容器。 When dereferencing an iterator, you get the value it points at.取消引用迭代器时,您将获得它指向的值。 So, when you dereference an iterator of type std::vector<int>::iterator you get a reference to an int .因此,当您取消引用std::vector<int>::iterator类型的迭代器时,您将获得对int的引用。


If you wrap your iterators in iterators that when dereferenced return the original iterators, you can construct the priority_queue using those.如果将迭代器包装在取消引用时返回原始迭代器的迭代器中,则可以使用这些迭代器构造priority_queue

Example:例子:

template<class It>
struct iterator_iterator {
    using value_type = It;
    using difference_type = std::ptrdiff_t;
    using pointer = void;
    using reference = value_type&;
    using iterator_category = std::forward_iterator_tag;

    iterator_iterator(It it) : curr(it) {}

    iterator_iterator& operator++() { ++curr; return *this; }
    iterator_iterator operator++(int) {
        iterator_iterator retval(*this);
        ++curr;
        return retval; 
    }
    
    bool operator==(const iterator_iterator& rhs) const {
        return curr == rhs.curr;
    }

    bool operator!=(const iterator_iterator& rhs) const {
        return curr != rhs.curr;
    }

    // this is used by the priority_queue's contructor:
    It operator*() { return curr; }

private:
    It curr;
};

then然后

unordered_map<int, int> freq_cnt({{3, 1}, {2, 4}, {5, 2}});

std::priority_queue<myIt, vector<myIt>, cmpHelper> h(
    iterator_iterator(freq_cnt.begin()),
    iterator_iterator(freq_cnt.end())
);

[freq_cnt.begin(), freq_cnt.end()) is a range of std::pair<const int, int> lvalues, not unordered_map<int,int>::iterator values. [freq_cnt.begin(), freq_cnt.end())是一系列std::pair<const int, int>左值,而不是unordered_map<int,int>::iterator值。

You can create a range of these iterators with C++20's std::ranges::iota_view :您可以使用 C++20 的std::ranges::iota_view创建一系列迭代器:

auto its = std::ranges::iota_view{ freq_cnt.begin(), freq_cnt.end() };
priority_queue<myIt,vector<myIt>, cmpHelper> h(its.begin(), its.end());

Otherwise, you can default construct the queue and push each element:否则,您可以默认构造队列并推送每个元素:

priority_queue<myIt,vector<myIt>, cmpHelper> h;
for (auto it = freq_cnt.begin(); it != freq_cnt.end(); ++it) {
    h.push_back(it);
}

But consider instead having a queue of references:但考虑改为有一个引用队列:

using myValue = typename unordered_map<int,int>::value_type;
using myRef = std::reference_wrapper<myValue>;

class cmpHelper {
    public:
    bool operator()(const myValue& l, const myValue& r){return l.second > r.second;}
};

int main(int argc, char* argv[]){
    unordered_map<int,int> freq_cnt({{3,1},{2,4},{5,2}});

    priority_queue<myRef,vector<myRef>, cmpHelper> h(freq_cnt.begin(), freq_cnt.end());
}

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

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