使用Move Idiom进行调整大小的语义无元素保存

发布于 2025-02-04 14:57:17 字数 1913 浏览 2 评论 0原文

std :: vector有一个名为ressize的成员函数。 ressize的实用程序是两倍,首先,它在有意义的情况下保留了现有元素的元素,其次,如果不需要,则避免分配。 (与简单地执行v = std :: vector< t>(new_size);都不会这样做。)

只有一个版本的ressize,一个是通过l-value参考来对象进行对象的一个​​版本(v.resize(new_size))。 https://en.cppreference.com/w/cpp/container/container/vector/调整大小

假设我正在编写一个类似于std :: vector的容器,我想实现ressize的版本,该版本是R-Value Aware。 我要说的是,由于原始对象处于“移动”状态,因此不需要元素保存。 原则上可以产生更有效的调整大小,因为没有必要保存元素(在调查大小都超过当前容量的情况下,这是特别有趣的。) 我认为操作是有效的

,但我不确定什么是一致的最终状态。

my_vector<double> v(3, 42.);

std::move(v).resize(6);

v的状态应该是什么?哪种静态与移动对象的当前理念更一致?

这里有一些选项:

  1. v具有6个元素,每个元素都完全没有指定。
  2. v{42。,42。,42.,0.,0。,0。}
  3. v is in 未指定,未指定,0。0。,0。}
  4. v is {0。,0。,0。,0。 code>

案例2是当前非R-Value-Aware vector :: Resize会做的。 我对案例3的倾向3.

明智的实施可能是4是“可能的”(即无证件)结果(作为案例3的未指定行为的一个例子)。 由于案例2也可能是一个一致的情况,因此我看来情况3或1是剩下的唯一正式选项。

考虑到可以调整更大值(6)的大小需要重新分配。

我正在使用double作为插图作为非平凡类型的地点持有人,该类型可能很昂贵。 和0。不是特别的,除非是默认构造状态(t())。 这个问题同样有效,这种情况略有不同:(

std::move(v).resize(6, 99.);

1. `v` has 6 elements and each element is totally unspecified.
2. `v` is `{42., 42., 42., 99., 99., 99.}`
3. `v` is `{unspecified, unspecified, unspecified, 99., 99., 99.}`
4. `v` is `{99., 99., 99., 99., 99., 99.}`

请注意,在任何情况下,std :: move(v)。分辨(6);有机会至少比> >更好v = my_vector&lt; t&gt;(6),因为后者总是分配并始终初始化所有元素。)

In std::vector there is a member function called resize.
The utility of resize is two fold, first, it preserves elements the existing elements when it makes sense, and, second, it avoids allocations if they are not necessary. (in contrast to simply doing v = std::vector<T>(new_size); which would do neither.)

There is only one version of resize, one that takes the object by l-value reference (v.resize(new_size)).
https://en.cppreference.com/w/cpp/container/vector/resize

Supposed I am writing a container similar to std::vector and I want to implement a version of resize that is r-value aware.
I would say that, since the original object is in a "moved" state, element preservation is not necessary.
Which in principle can produce a more efficient resize because element preservation is not necessary (this is particularly interesting in the case that the resize surpasses the current capacity.)
I think the operation is valid

But I am not sure what is a consistent final state.

my_vector<double> v(3, 42.);

std::move(v).resize(6);

What should be the state of v? What static is more consistent with the current philosophy of moved objects?

Here some options:

  1. v has 6 elements and each element is totally unspecified.
  2. v is {42., 42., 42., 0., 0., 0.}
  3. v is {unspecified, unspecified, unspecified, 0., 0., 0.}
  4. v is {0., 0., 0., 0., 0., 0.}

Case 2 is what the current non r-value-aware vector::resize would do.
I have a slight inclination towards case 3.

Implementation wise it is possible that 4 is the "likely" (i.e. undocumented) result (as an instance of the unspecified behavior of case 3).
Since case 2 could also be a consistent instance, I seem that case 3 or 1 are the only formal options left.

Take into account that it is possible that resize to a larger value (6) requires reallocation.

I am using double for illustration as a place holder for a non-trivial type that might be expensive to copy.
And 0. is not special except for being the default constructed state (T()).
The question is equally valid with this slightly different case:

std::move(v).resize(6, 99.);

1. `v` has 6 elements and each element is totally unspecified.
2. `v` is `{42., 42., 42., 99., 99., 99.}`
3. `v` is `{unspecified, unspecified, unspecified, 99., 99., 99.}`
4. `v` is `{99., 99., 99., 99., 99., 99.}`

(Note that in any case std::move(v).resize(6); has the chance to do at least slight better than v = my_vector<T>(6) because the latter always allocate and always initializes all the elements.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

白云悠悠 2025-02-11 14:57:17

误解了std :: Move的意思是并且确实如此。它没有移动任何东西。

std ::移动做什么是告诉编译器我不再需要此

因此,当您说:std :: move(v).resize(6);就像您说您不在乎resize()的结果。 (这不是编译器会做的,而是这样考虑的)。您能明白为什么这没有道理吗?

因此,在std ::移动调用后,您的对象可以移动。但是,而不是移动它,而是调用resize()函数。 resize()函数永远不会移动此,它保持在原处。它只会更改对象的内部状态,主要是分配的数据的大小。因此,std ::移动完全具有0个效果。

您似乎真正关心的不是向量,而是向量所包含的对象。当您调用resize()时,有3个案例要考虑:

  1. 调整大小的元素少于向量所保留的

额外元素需要调用其destructor。什么都没有移动或重新分配。向量的能力不会改变。

  1. 种植矢量,但在此处

以下新要素是初始化的。什么都没有移动或重新覆盖。

  1. 将向量扩大到其能力之外

,在这里,向量必须分配新的内存。然后,它必须将元素复制或移动到新存储。此外,新元素是初始化的。

现在,您可以选择一个可以做出的选择。您说移动元素很昂贵。您可以做出的选择是clear() resize()之前的向量。这将调用所有元素的击振子,然后调整大小将初始化全尺寸的所有新元素。问问自己,破坏 +建筑是否比移动更昂贵。

在所有情况下,无论您做什么,最终向量都将完全初始化。也许您在考虑储备()而不是resize()?您也可以在保留前调用clear(),它将调用现有元素的destuructor,并在储备金() 的大小0之后,将整个矢量单次化。

There is a misconception of what std::move means and does. It does not move anything.

What std::move does is tell the compiler I don't need this anymore.

So when you say: std::move(v).resize(6); It's like you say you don't care about the result of resize(). (It's not what the compiler will do, but think of it that way). Can you see why this makes no sense?

So after the std::move call you have an object that can be moved. But instead of then moving it you call the resize() function. The resize() function never moves this, it remains right where it is. It will only change the internal state of the object, mainly the size of the heap allocated data. So the std::move has 0 effect at all.

What you really seem to care about isn't the vector but the objects the vector holds. When you call resize() there are 3 cases to consider:

  1. resizing to fewer elements than the vector holds

Here the extra elements need to have their destructor called. Nothing is moved or reallocated. The capacity of the vector doesn't change.

  1. growing the vector but within the capacity

Here the new elements are initialized. Nothing is moved or reallcoated.

  1. growing the vector beyond it's capacity

Here the vector has to allocate new memory. And then it has to copy or move elements to the new storage. Additionally the new elements are initialized.

Now here you do have a choice you can make. You say moving elements is expensive. The choice you can make is to clear() the vector before the resize(). That calls the destructor for all elements and then the resize will initialize all new elements for the full size. Ask yourself if destruction + construction is more expensive than move.

In all cases the final vector will be fully initialized no matter what you do. Maybe you were thinking of reserve() instead of resize()? You can call clear() before reserve too, which will call the destructor for existing elements and leave the whole vector uninitialized after reserve() with a size of 0.

满栀 2025-02-11 14:57:17

推理中的一个缺陷是std ::移动(x)本身不会移动任何东西。如果这些名称没有太长,它可以命名为enable_move allow_move 。它标记为“可移动”,但不执行移动操作。因此,只要ressize没有RVALUE过载,则使用std :: Move可以移动。

但是,std :: vector已经意识到,并且(可能)将使用助手函数 move_if_noexcept()移动具有noexcept move构造函数的对象。

例如,通过std :: vector :: resize使用,可能必须必须
分配新存储,然后从旧存储中移动或复制元素
到新存储。如果此操作期间发生例外,
std :: vector :: resize撤消了此时所做的一切,
仅当std :: move_if_noexcept用于决定是否是否
使用移动建筑或复制构造。 (除非复制构造函数
不可用,在这种情况下,无论哪种方式都使用移动构造函数
并且可以放弃强大的例外保证)。

A flaw in the reasoning is that std::move(x) in itself doesn't move anything. It could have been named enable_move or allow_move, if those names hadn't been too long. It marks things as "movable", but doesn't perform the move operation. So, as long as resize doesn't have an rvalue overload, nothing is moved by using std::move.

However, std::vector is already rvalue aware and will (likely) use the helper function move_if_noexcept() to move objects that have a noexcept move constructor.

This is used, for example, by std::vector::resize, which may have to
allocate new storage and then move or copy elements from old storage
to new storage. If an exception occurs during this operation,
std::vector::resize undoes everything it did to this point, which is
only possible if std::move_if_noexcept was used to decide whether to
use move construction or copy construction. (unless copy constructor
is not available, in which case move constructor is used either way
and the strong exception guarantee may be waived).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文