【问题标题】:Is it legal to move elements from standard containers?从标准容器中移动元素是否合法?
【发布时间】:2019-08-27 11:54:27
【问题描述】:

我正在编写一个需要高效函数来过滤成员容器的类(比如说std::vector)。这个函数应该有类似下面的接口:

void filter(std::vector<SomeType>& items, const std::vector<int>& inds);

该函数应使容器items 处于以下状态: - 应删除索引从inds 指向的项目 - 其他物品应保留在容器中,保持初始顺序。

为简单起见,我们假设 inds 是一个完美的容器,每个操作的 O(1) 都为 O(1),并且所有索引都有效且没有重复。

我的想法是创建第二个容器,保留所需空间,然后将未被inds 索引的每个元素移动(通过std::move)到这个新容器中;然后只需交换旧容器和新容器。

例如这样:

void filter(std::vector<SomeType>& items, const std::vector<int>& inds)
{
    std::vector<SomeType> new_items{};
    new_items.reserve( items.size() - inds.size() );

    for(size_t i = 0; i < items.size(); ++i)
        if( contains( inds, i ) ) // magic O(1) function, never mind
            new_items.push_back( std::move( items[i] ) );

    items.swap(new_items);
}

我的问题是:

1) 在vector(或通常任何其他标准容器)内的某些元素上使用std::move 后,是否会出现诸如双重破坏这些元素之类的问题?

2) 有没有一种标准方法可以有效地进行此类过滤?

【问题讨论】:

  • 你不需要两个向量,因为你只是删除,所以你可以在删除最后一个剩余项目之后移动第一个项目,并从那里继续复制所有不被删除的项目,并跳过应该删除的项目。如果您的向量非常大(将内存使用量减半),或者您只删除少数项目,即最初跳到要删除的第一个项目(不修改初始部分),这可能会稍好一些(不修改初始部分)可能会节省一小部分的时间。 IE。 12345678(减去 3 5 7)= 12468 -> 您可以安全地在原始“345678”上写“468”。
  • 顺便说一句,std::vector&lt;T&gt; 本质上需要可以“移动”的类型(“T 必须满足 CopyAssignable 和 CopyConstructible 的要求。”),因为vector 本身会重新分配整个缓冲区它用完了空间,并将所有值从一个内存缓冲区“移动”到新的缓冲区(通常在简单类型的情况下类似于 memmove,在更复杂类型的情况下进行复制分配)。 IE。如果您的SomeType 已经可以在std::vector 中使用,那么我认为std::move 也应该适用(希望我没有忘记一些正式细节,为什么std::move 在这种类型上可能会失败)。跨度>

标签: c++ containers stdmove


【解决方案1】:

容器不负责防止移动可能出现的问题。只要移动项目的类型具有定义明确且正确的移动构造函数/隐式移动构造函数,那么将项目从items 移动到new_items 与任何其他移动操作相同;容器不会改变这一点。

简而言之,这个责任在于类,而不是它所在的容器。

【讨论】:

    【解决方案2】:

    您从中移动的对象处于有效但未指定的状态,并且可以在它们上调用没有前置条件的函数。析构函数必须是没有前提条件的函数。如果SomeType 不满足这个条件,那么它就是病态的。

    所以不存在双重破坏的问题。

    【讨论】:

    • 您从中移动的对象处于有效但未指定的状态,并且可以在它们上调用没有先决条件的函数 仅当这是类的移动时才如此构造函数提供。如果他们提供字符串保证,那么你就有了一个强大的保证。如果您忘记了 5 规则并且在资源分配类中没有移动构造函数,那么您将无法保证。容器会“做正确的事”,前提是对象会这样做。
    • Re:“您从中移动的对象处于有效但未指定的状态,并且可以在它们上调用没有前提条件的函数”——这是 C++ 标准对定义在标准库。这是一个很好的设计策略,但仅适用于用户定义的类型,前提是它们实际上被实现为这样做。
    猜你喜欢
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-05
    相关资源
    最近更新 更多