【问题标题】:.data() equivalent for std::queue.data() 等效于 std::queue
【发布时间】:2014-08-26 16:48:14
【问题描述】:

我的问题很简单:是否可以获得指向std::queue容器适配器底层存储的指针?

我正在使用 SFML 进行渲染的一些模拟,我使用 SFML 渲染目标 (sf::RenderTarget) 的 draw() 方法来绘制整组数据。该方法有一个类似 C 的接口,需要一个指向数据的指针和一个带有要绘制元素数量的 std::size_t

由于出于某些目的将数据存储在队列中,如果有某种方法可以将该指针指向队列底层存储,而不是将数据复制到向量中,我将很高兴。
我知道std::queue默认适应容器std::deque,但我不知道那个循环缓冲区是如何实现的,它的数据是否连续(所以我可以直接提取指向数据的指针)。

编辑:性能

看看下面的答案,让我注意我使用std::deque 不是因为它花哨的类似队列的界面,而是因为我真的需要快速排队。当然我可以使用std::vector。如果性能不是这里的重点,我会使用矢量的push_back()erase( begin() )。但我需要的是快速排队和一种将队列中的数据有效地移动到渲染目标的方法。当然,如果排队与努力抽签平衡到抽签方面,我将使用std::vector

【问题讨论】:

  • IIRC,std::deque 没有连续存储,但通常以块的形式分配,并带有指向这些块的指针数组。
  • 不,没有这样的成员,这是有原因的。默认后端容器std::deque<> 提供的存储不是标准要求的连续存储。您也提到过,实施也不是强制性的。
  • 如果你好奇 std::deque<> 通常是如何实现的,see this answer to a different question 是一个常见的实现。它很好地说明了为什么您使用 std::deque<> 后端寻求的东西根本不存在。
  • 没有人注意到std::queue只是包装了底层容器,所以它调用container.pop_front()然后就不能和std::vector一起使用了? (n.m. 很快删除了他的答案;))。如果就这么简单,我现在会使用std::vector,而且我永远不会在这里问问题(因为在这里问是我们应该做的最后一件事。当我真的不知道如何继续时,我会问这样的问题) .
  • 老实说,根据您为该 C API 调用缓冲的数据大小,我认为如果您真的想要,您的复制然后调用方法很可能是您的最佳选择快速排队呼叫的连续数据。至少在我看来,您最好直接使用deque 直接删除中间人(队列适配器),并保持您的先复制后调用方法。我想,值得进行基准测试。我们在这里谈论多少数据(每份)?基于迭代器的复制(队列提供但双端队列提供的东西)可能会有很大帮助。

标签: c++ c++11 containers c++-standard-library


【解决方案1】:

是否有可能获得指向底层存储的指针 std::queue 容器适配器?

简短回答:不。

std::queue 需要 SequenceContainer 类型,例如 std::dequestd::list。这些都不能保证连续存储,因此没有指向底层存储的指针的概念

如果他们使用连续的循环缓冲区,这将在容器上施加固定大小,或者在需要调整容器大小时产生大量成本(如 std::vector 可以)。

您可以考虑改用boost::circular_buffer

【讨论】:

    【解决方案2】:

    最直观的想法是使用std::vector 作为std::queue 的底层容器以确保连续内存,然后通过获取std::queue::front 的地址来访问包装std::vector 的连续存储。

    但是,正如@Blastfurnace 正确指出的那样,std::vector 没有pop_front 成员函数,当您要调用std::queue::pop 时,程序会爆炸。

    同样如前所述,std::queue 通常需要像std::dequestd::list 这样的容器作为其底层容器。由于它们的结构特征,这些容器是最合适的,因为它们可以有效地弹出它们的前端元素。但是,由于其连续内存std::vector 缺乏这种多功能性。

    此外,如果使用具有连续内存的容器可以显着提升您的应用程序,那么使用std::vector 会更好、更直接。

    但是,为了记录,您还可以执行以下 hack 以规避 std::vectorpop_front 成员函数的缺失。您可以定义一个继承自std::vectorclass 并实现pop_front 成员函数,如下例所示:

    #include <vector>
    #include <queue>
    #include <iostream>
    #include <algorithm>
    
    template<class T, class Alloc = std::allocator<T>>
    class queuevec : public std::vector<T, Alloc> {
    public:
      void pop_front() {
       if(!this->empty()) this->erase(this->begin());
      }
    };
    
    int main() { 
      std::queue<int, queuevec<int>> Q;
    
      for(int i(0); i < 10; ++i) Q.push(i);
    
      Q.pop();
    
      int *t = &(Q.front());
      std::for_each(t, t + Q.size(), [](int const i){ std::cout << i << " "; });
      std::cout << std::endl;
    }
    

    LIVE DEMO

    【讨论】:

    • 虽然这样可行,但问题是 pop_front 的性能会很糟糕。即使在 C++11 中,也会有大量内存在运行。
    • @Roddy,我完全同意你的看法。恕我直言,我认为我在回答中涵盖了这一方面,建议 OP 仅在有重要意义的情况下才使用连续的内存容器,当然总是在分析她/他的程序之后。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 2010-09-20
    • 2014-02-02
    • 2010-10-22
    • 1970-01-01
    相关资源
    最近更新 更多