是否可以从队列中“移动”一个对象,如果你要从中弹出它?

时间:2016-05-11 21:36:18

标签: c++ c++11 boost stl optional

我一直在研究commands的解析器(它是围绕大型数据数据的奇特包装器),并且有一个未处理命令所在的队列。如果我需要一个命令,我用这样的代码查询它:

boost::optional<command> get_command() {
    if (!has_command()) return boost::optional<command>(nullptr);
    else {
        boost::optional<command> comm(command_feed.front()); //command_feed is declared as a std::queue<command>
        command_feed.pop();
        return comm;
    }
}

问题是,在适当的情况下,这些命令的大小可能是兆字节,并且需要非常快速地解析。我的想法是,我可以将转移优化为这样的举动:

boost::optional<command> get_command() {
    if (!has_command()) return boost::optional<command>(nullptr);
    else {
        boost::optional<command> comm(std::move(command_feed.front())); //command_feed is declared as a std::queue<command>
        command_feed.pop();
        return comm;
    }
}

它似乎适用于这种特定情况,但这可以用作任何正确维护的RAII对象的通用解决方案,还是应该做其他事情?

4 个答案:

答案 0 :(得分:26)

是的,这非常安全:

std::queue<T> q;
// add stuff...

T top = std::move(q.front());
q.pop();

pop()q中具有指定状态的第一个元素没有任何先决条件,并且由于您之后没有使用q.front(),因此您无需处理该对象再次失效。

听起来不错!

答案 1 :(得分:6)

这取决于您的类型的移动构造函数。如果它使原始对象处于可以安全销毁的状态,那么一切都很好。如果没有,那么你可能遇到麻烦了。请注意,有关前置条件和有效状态的注释是关于对标准库中定义的类型的约束。您定义的类型没有这些约束,除非它们使用标准库中的类型。因此,请查看移动构造函数,以便了解移动对象可以做什么和不能做什么。

答案 2 :(得分:4)

即可。只要您的std::queue容器模板参数确保pop_front()的包含值的状态没有先决条件; std::queue的默认值为std::deque,提供保证。

只要你确保我在前一段写的内容,你就完全安全了。您即将从队列中删除该项目,因此没有理由不将其移出,因为您取得该对象的所有权

答案 3 :(得分:1)

移动对象可能会使其处于无效状态。不再保证它的不变量。你可以安全地从非侵入式队列中弹出它。

  • std :: move本身除了告诉编译器之外什么都不做,它可以选择一个带r值的comm例程。

  • 一个编写良好的通用例程,然后会从旧对象中窃取新对象的表示。例如,只需将指针复制到新对象,并将旧对象中的指针归零(这样旧的对象析构函数就不会破坏数组)。

  • 如果comm没有超载来执行此操作,则对std :: mov没有任何好处。