可能是一个蹩脚的问题,但我一直未能找到全面的答案。
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?
我真的很困惑。我的测试显示str
在emplace_back
之后可以安全使用,但是我的(破坏?)逻辑告诉我str
被移动了,之后不应该使用。< / p>
PS。抱歉我的英语不好:)
答案 0 :(得分:9)
emplace
- 样式函数的参数是转发引用,这意味着它们成为左值参数的左值引用和rvalue参数的右值引用。
使用
array.emplace_back(str);
str
是一个左值(你没有将它转换为std::move
的右值),因此它将被复制。它将在通话后保留其价值。
答案 1 :(得分:1)
emplace_back
将复制l值,并移动r值。
有人可能会用一个简单的例子对此进行测试:
struct test {
test() {
std::cout << "Default-constructed\n";
}
test(test const&) {
std::cout << "Copy-constructed\n";
}
test(test &&) {
std::cout << "Move-constructed\n";
}
~test() {
std::cout << "Destructed\n";
}
test& operator=(test const&) {
std::cout << "Copy-assigned\n";
return *this;
}
test& operator=(test &&) {
std::cout << "Move-assigned\n";
return *this;
}
};
int main() {
std::vector<test> vector;
test t;
vector.emplace_back(t);//The important one
vector.emplace_back(test{});
return 0;
}
这(假设copy-ellision不适用于此处)导致以下输出:
Default-constructed
Copy-constructed //The important one
Move-constructed
Destructed
Destructed
Destructed
请注意,当使用l值调用emplace_back
时,会调用复制构造函数。因此,在您的情况下,字符串将被复制,而不是移动,因此可以安全地继续在向量之外使用。
值得注意的是,Move-Semantics通常要求移动的对象“处于未指定但有效的状态”,这意味着使用移动对象实际上不应该是“不安全”。它仍然可以产生奇怪的效果,并且可以调用未定义的行为,具体取决于该对象的有效状态可以包含的内容(例如,如果您尝试取消引用移动的unique_ptr
或其他类似对象)。
答案 2 :(得分:1)
标准库对象通常处于“有效但未指定的状态”。
有效但未指明的状态
除了满足对象的不变量和对其的操作之外,未指定的对象的值 对象的行为与其类型相同 [示例:如果LOCALE_IDEFAULTCODEPAGE
类型的对象x处于有效但未指定的状态,std::vector<int>
可以是 无条件调用,只有x.empty()
返回x.front()
时才能调用x.empty()
。 -end example ]
这通常意味着空或保留原始值。移动false
可能不会重置其值。
某些类型更具指定性,例如int
在移动后始终保持nullptr。
所以,在这种情况下
unique_ptr
代码有效,但我们不确切知道输出是什么,如果有的话。使它不那么有用。
这个想法是你应该在移动之后让变量超出范围,或者为其分配一个新值以进一步使用它。