【问题标题】:Is it okay to "Move" an object from a queue, if you're about to pop from it?如果您要从队列中弹出对象,可以从队列中“移动”一个对象吗?
【发布时间】:2016-09-07 12:11:03
【问题描述】:

我一直在为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 对象的通用解决方案,还是我应该做其他事情?

【问题讨论】:

  • 是的,移动它是安全的,只要唯一你用它做的事情就是弹出它
  • @SteveLorimer 或重新分配它。或者在没有前提条件的情况下进行任何其他操作。
  • @Revolver_Ocelot 赋值不一定没有前提。
  • 这些预防措施适用于任何时候移动对象。与队列弹出无关。

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


【解决方案1】:

是的,这是绝对安全的:

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

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

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

听起来是个好主意!

【讨论】:

  • 绝对安全...取决于T的特性。所以不能保证安全。
  • @juanchopanza 当然,只要 T 是 MoveConstructible。
  • 这是我心中的要求。移动 from. 后它必须处于某种状态的点
  • 嗯这也适用于priority_queues 吗?你必须从priority_queue::top()中抛弃const
  • @TheCppZoo 问题是关于std::queue,它没有top。对于std::priority_queue,您可能需要const_cast,是的(这也是安全的)。
【解决方案2】:

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

【讨论】:

  • 如果被移动的对象是不可破坏的,那么这个移动构造函数/赋值基本上是不可用的。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

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

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

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

    【讨论】:

    • 移动对象可能会使其处于无效状态。它的不变量不再得到保证。 有效但未指定状态!
    猜你喜欢
    • 1970-01-01
    • 2018-05-07
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多