【问题标题】:Getting index of an element in a std::queue by its value通过值获取 std::queue 中元素的索引
【发布时间】:2019-06-16 00:59:04
【问题描述】:

有没有一种简单的方法可以通过 C++ 中的值来获取 std::queue 中元素的位置?

例如:

std::queue<int> numbers;

numbers.push(7);
numners.push(4);
numbers.push(11);

int position = numbers.getPosition(4); //should be 1

【问题讨论】:

  • XY problem 的气味非常强烈。如果您确实需要从 std::queue 的值中获取元素的索引(或迭代器),那么您的代码设计有缺陷:如果需要,请不要使用 std::queue。 (此外,可能没有唯一索引,具体取决于值:如果它们都相同怎么办?)
  • 请注意,STL 容器并非设计为公开派生自,因为它们没有虚拟析构函数。我在这里不这样做,但只要了解风险,仍然有人可以公开从std::queue 获取信息。

标签: c++ stl queue


【解决方案1】:

如果您想获取元素的索引,您可能应该考虑使用std::deque 容器而不是std::queue 容器适配器,正如this other answer 中已经建议的那样。


如果您出于其他原因仍想坚持使用std::queue 容器适配器,您应该知道它确实通过protected 数据成员c 提供对底层容器的访问。

您可以从std::queue 派生以访问底层容器并使用std::find() 函数模板在该容器中查找具有此类值的元素。然后,只需使用 std::distance() 返回该元素的位置。

#include <algorithm>
#include <queue>

template<typename T>
class Queue: std::queue<T> {
public:
   auto getPosition(const T& val) const {
      auto it = std::find(this->c.begin(), this->c.end(), val);
      return std::distance(this->c.begin(), it);
   }
// ...
};

如果没有找到该元素,则索引将对应于size()成员函数返回的那个。

如果有重复,这个基于std::find()的解决方案将返回第一个的位置,即找到请求值val的第一个元素。

【讨论】:

    【解决方案2】:

    您可以改用std::deque

    #include <algorithm>
    
    std::deque<int> names;
    
    names.push_back(7);
    names.push_back(4);
    names.push_back(11);
    
    auto it = std::find(names.begin(), names.end(), 4);
    if(it != names.end())
        int distance = it - names.begin();
    else
        //no element found
    

    注意std::queue 使用std::deque 作为默认实现,因此任何操作都与队列中的时间相同。

    std::deque 也支持随机访问,所以names[0] 将返回 7。它也可以像任何其他队列一样使用:

    std::deque<int> myDeque{};
    myDeque.push_back(5);
    myDeque.push_back(13);
    std::cout << myDeque.front(); //5
    myDeque.pop_front();
    std::cout << myDeque.front(); //13
    

    【讨论】:

      【解决方案3】:

      您还可以使用these answers提出的以下函数访问底层受保护成员std::queue::c

      #include <queue>
      
      template <class ADAPTER>
      const auto& get_container(ADAPTER& a)
      {
          struct hack : private ADAPTER {
              static auto& get(ADAPTER& a) {
                  return a.*(&hack::c);
              }
          };
      
          return hack::get(a);
      }
      

      然后你可以得到一个元素的索引,如下所示。 该功能也可以应用于其他容器适配器,std::stackstd::priority_queue

      DEMO(C++11)

      DEMO(C++14 and over)

      #include <iostream>
      #include <algorithm>
      #include <iterator>
      
      int main()
      {
          std::queue<int> q;
          q.push(7);
          q.push(4);
          q.push(11);
      
          auto& c = get_container(q);
      
          const auto it = std::find(c.cbegin(), c.cend(), 4);
          const auto position = std::distance(c.cbegin(), it);
          std::cout << "Position is " << position << "." <<std::endl;
      
          return 0;
      }
      

      【讨论】:

        【解决方案4】:

        另一种通用方法是定义以下新容器,它是std::queue 的继承,并定义了begin()end() 返回受保护成员std::queue::c 的迭代器。 然后你可以在这个容器中使用各种 STL 算法:

        #include <queue>
        
        template<
            class T,
            class Container = std::deque<T>
        >
        class Queue : public std::queue<T, Container>
        {
        public:
            using iterator               = typename Container::iterator;
            using const_iterator         = typename Container::const_iterator;
            using reverse_iterator       = typename Container::reverse_iterator;
            using const_reverse_iterator = typename Container::const_reverse_iterator;
        
            iterator        begin()       noexcept { return this->c. begin(); }
            const_iterator  begin() const noexcept { return this->c.cbegin(); }
            const_iterator cbegin() const noexcept { return this->c.cbegin(); }
        
            iterator        end()       noexcept { return this->c. end(); }
            const_iterator  end() const noexcept { return this->c.cend(); }
            const_iterator cend() const noexcept { return this->c.cend(); }
        
            reverse_iterator        rbegin()       noexcept  { return this->c. rbegin(); }
            const_reverse_iterator  rbegin() const noexcept  { return this->c.crbegin(); }
            const_reverse_iterator crbegin() const noexcept  { return this->c.crbegin(); }
        
            reverse_iterator        rend()       noexcept  { return this->c. rend(); }
            const_reverse_iterator  rend() const noexcept  { return this->c.crend(); }
            const_reverse_iterator crend() const noexcept  { return this->c.crend(); }
        };
        

        ...是的,众所周知,STL 容器没有虚拟析构函数。 通过基类指针销毁这个派生类会导致未定义的行为。 因此,当且仅当您确实需要时,我建议使用上述派生类。


        对于当前位置问题,可以找到找到第一个元素的位置如下:

        DEMO

        #include <algorithm>
        #include <iterator>
        
        Queue<int> q;
        q.push(7);
        q.push(4);
        q.push(11);
        
        const auto it = std::find(q.cbegin(), q.cend(), 4);
        const auto position = std::distance(q.cbegin(), it); //should be 1
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-03
          • 2023-01-02
          • 1970-01-01
          • 2012-02-14
          • 1970-01-01
          • 1970-01-01
          • 2015-04-05
          • 1970-01-01
          相关资源
          最近更新 更多