【发布时间】:2021-12-19 06:42:19
【问题描述】:
我正在尝试理解移动语义的一般规则。 特别是容器和包含的元素。
原因是我试图在所有权和迭代器失效的背景下理解移动。
为此,我正在处理一些复杂性增加的案例,其中涉及典型容器、一般包含类型 T、一般 g 和 f 函数。
(也许一个重要的额外细节是 f 可能会或可能不会实际执行移动操作,或者它可能在运行时是偶然的。)
思路是引入案例3,这是本题的核心。
案例 0
首先是一个没有争议的案例,这没关系:
std::vector<T> v(100, t);
f(std::move(v));
v = make_a_vector();
案例一
然而,use-after-move 可能是臭代码
std::vector<T> v(100, t);
f(std::move(v));
g(v);
我想大多数人都同意上面的这个是不行的。
规则是(据我所知)移动后唯一的操作应该是赋值。
我认为这尤其是因为它是未记录的(未定义但有效的状态)什么是移动的向量的状态,(或者即使它被移动了。)。
因此,充其量 v 是空的,最坏的情况是 v 是未指定的状态,因此 g 可以在此范围内执行未指定的操作。
案例2:
std::vector<T> v(100, t);
f(std::move(v));
v.resize(120);
正确吗?
这不是赋值,但resize 没有先决条件。 (找到这个Can I resize a vector that was moved from?)
案例 3:
现在是真正棘手的情况。
std::vector<T> v(100);
h(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
v.resize(120);
(这里,h 是一个带有迭代器的函数,假设它隐式引用了 范围 [iterator1, iterator2)。)
这是正确的代码吗?
原因是.resize 似乎要播放、移动、交换和复制 T 类型的已移动对象。
总而言之,调整元素已(可能)移出的向量的大小是否正确?
编辑:为了论证,让我们指定函数的签名,以防它们与答案相关:
template<class T> void f(std::vector<T>&&);
template<class T> void g(std::vector<T> const&);
template<class It> void h(It first, It last);
【问题讨论】:
-
你能定义 g 和 f 原型吗?
-
我之所以要求您定义原型,是因为通常原型会根据您与您调用的函数的约定告诉您是否可以调整它的大小。如果你传递了一个 r 值引用,而你不是它的管家,那么除了内存消耗之外,你还关心它会发生什么?
-
@alex,为了争论,我们假设
template<class T> void f(std::vector<T>&&);和template<class T> void g(std::vector<T> const&);和template<class It> void h(It first, It last);。 -
您关于案例 0 的声明不正确。
std::move(v)对v没有直接影响,因此f(std::move(v))的效果(如果有)取决于f()本身 - 它是通过值还是通过引用(&或&&)接受参数和- 如果通过引用 - 它对它的论点做了什么。即使它确实有效果,它也会使v处于有效但未指定的状态(这意味着除其他外,v可以安全地分配或销毁)。由于您认为您的第一个主张没有争议,而我对此提出异议,因此我没有进一步阅读。 -
有关我之前评论的更多信息,请查看stackoverflow.com/questions/7027523/…
标签: c++ vector stdvector move-semantics