使用移动元素调整向量大小是否正确?

alf*_*lfC 1 c++ vector stdvector move-semantics

我试图理解移动语义的通用规则。特别是容器和包含的元素。

原因是我试图在所有权和迭代器失效的背景下理解移动。为此,我将经历一些复杂性不断增加的案例,涉及典型容器、通用包含类型T、通用gf函数。(也许一个重要的额外细节是,f实际上可能会或可能不会执行移动操作,或者它可能在运行时是偶然的。)

这个想法是引入案例3,这是这个问题的核心。

案例0

首先是一个相当没有争议的案例,这是可以的:

std::vector<T> v(100, t);
f(std::move(v));
v = make_a_vector();
Run Code Online (Sandbox Code Playgroud)

情况1

然而,移动后使用可能是臭代码

std::vector<T> v(100, t);
f(std::move(v));
g(v);
Run Code Online (Sandbox Code Playgroud)

我想大多数人都同意上面的做法是不行的。规则是(据我所知)移动后的唯一操作应该是赋值。我认为这尤其是因为它没有记录(未定义但有效的状态)移出向量的状态是什么(或者即使它被移动了)。因此,充其量v是空的,最坏的v情况是未指定的状态,因此g可以在此范围内执行未指定的操作。

案例2:

std::vector<T> v(100, t);
f(std::move(v));
v.resize(120);
Run Code Online (Sandbox Code Playgroud)

这是对的吗?这不是一项任务,但resize没有先决条件。(发现这个Can I resize a vector that was moving from?

案例3:

现在是真正棘手的情况。

std::vector<T> v(100);
h(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
v.resize(120);
Run Code Online (Sandbox Code Playgroud)

(这里,h是一个采用迭代器的函数,假设它隐式引用 range [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);
Run Code Online (Sandbox Code Playgroud)

M.M*_*M.M 5

如果它是标准库对象的向量,则可以保证调整向量的大小是安全的。请参阅什么构成了 C++11 中“移出”对象的有效状态?

如果是您自己的对象的向量,那么问题就回到了您是否将自己的对象设计为在移动后有效。