变量用作emplace_back参数后可以使用变量吗?

Is it fine to use variables after they being used as arguments of emplace_back?

本文关键字:变量 可以使 back emplace 参数      更新时间:2023-10-16

可能是一个蹩脚的问题,但我总是找不到全面的答案。

std::vector::emplace_back的参数是 r 值引用。据我了解,在通过 r 值引用传递到某处后使用对象是不安全的。我的意思是:

std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str;                      // unsafe, str was moved to str2

那么,以下示例会发生什么?

std::vector<std::string> array;
std::string str("hello world");   // what if add 'const' qualifier here?
array.emplace_back(str);          // template <class... Args>
// void emplace_back (Args&&... args);
std::cout << str;                 // safe or not? str was moved or copied?

我在这里真的很困惑。我的测试表明,stremplace_back后可以安全使用,但我(坏了?)逻辑告诉我str被移动了,之后不应该使用。

对不起,我的英语:)不好

emplace样式函数的参数是转发引用,这意味着它们成为左值参数的左值引用和右值参数的右值引用。

array.emplace_back(str);

str是一个左值(您尚未将其转换为带有std::move的右值),因此它将被复制。它将在调用后保留其值。

标准库对象通常处于"有效但未指定的状态"。

有效但未指定的状态 未指定的对象的
值,除非满足对象的不变量并且对 对象的行为与其类型指定 [示例:如果类型为std::vector<int>的对象 x 处于有效但未指定的状态,则x.empty()可以是 无条件调用,并且只有在x.empty()返回false时才能调用x.front()—结束示例]

大多数情况下,这意味着要么为空,要么保留原始值。移动int可能不会重置其值。

某些类型更具体,例如unique_ptr从中移出后始终保留 nullptr。

所以,在这种情况下

std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str;   

代码是有效的,但我们不知道输出的确切内容(如果有的话)。使它不太有用。

这个想法是,你应该让变量在被移出后超出范围,或者给它分配一个新值以进一步使用它。

emplace_back将复制 l 值,并移动 r 值。

可以用一个简单的例子来测试这一点:

struct test {
test() {
std::cout << "Default-constructedn";
}
test(test const&) {
std::cout << "Copy-constructedn";
}
test(test &&) {
std::cout << "Move-constructedn";
}
~test() {
std::cout << "Destructedn";
}
test& operator=(test const&) {
std::cout << "Copy-assignedn";
return *this;
}
test& operator=(test &&) {
std::cout << "Move-assignedn";
return *this;
}
};
int main() {
std::vector<test> vector;
test t;
vector.emplace_back(t);//The important one
vector.emplace_back(test{});
return 0;
}

这(应该,假设复制省略号在这里不适用)会导致以下输出:

Default-constructed
Copy-constructed //The important one
Move-constructed
Destructed
Destructed
Destructed

请注意,当使用 l 值调用emplace_back时,将调用复制构造函数。因此,在您的情况下,字符串将被复制,而不是移动,因此可以安全地在向量之外继续使用。

还值得注意的是,移动语义通常要求移自对象"处于未指定但有效的状态",这意味着使用移自对象实际上不应该是"不安全的"。不过,它仍然可能产生奇怪的效果,并且可以根据该对象的有效状态可以包含的内容调用未定义的行为(例如,如果您尝试取消引用移自unique_ptr或其他类似对象)。