Why can we use `std::move` on a `const` object?
在C ++ 11中,我们可以编写以下代码:
1 2 3 4 5 6 | struct Cat { Cat(){} }; const Cat cat; std::move(cat); //this is valid in C++11 |
当我调用
陷阱的含义如布兰登在评论中所述:
" I think he means it"traps" him sneaky sneaky because if he doesn't
realize, he ends up with a copy which is not what he intended."
在斯科特·迈耶斯(Scott Meyers)的著作《有效的现代C ++》中,他举了一个例子:
1 2 3 4 5 6 7 8 9 10 11 | class Annotation { public: explicit Annotation(const std::string text) : value(std::move(text)) //here we want to call string(string&&), //but because text is const, //the return type of std::move(text) is const std::string&& //so we actually called string(const string&) //it is a bug which is very hard to find out private: std::string value; }; |
如果
您忽略了一个技巧,即
1 2 3 4 5 6 7 8 9 10 11 | struct CAT { CAT(){} CAT(const CAT&) {std::cout <<"COPY";} CAT(CAT&&) {std::cout <<"MOVE";} }; int main() { const CAT cat; CAT cat2 = std::move(cat); } |
打印
http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f
请注意,您提到的代码中的错误是性能问题,而不是稳定性问题,因此此类错误永远不会导致崩溃。它将使用较慢的副本。此外,对于没有移动构造函数的非const对象,也会发生此类错误,因此仅添加
哎呀,也许有人希望能够从
1 2 3 4 5 6 7 | struct strange { mutable size_t count = 0; strange( strange const&& o ):count(o.count) { o.count = 0; } }; const strange s; strange s2 = std::move(s); |
在这里,我们看到在
它被称为。
现在,的确,这种奇怪的类型比您的建议所要解决的错误更罕见。
但是,另一方面,现有的
到目前为止,其余答案都被忽略的原因之一是通用代码在面对移动时具有弹性。例如,假设我想编写一个通用函数,该函数将所有元素移出一种容器,以创建具有相同值的另一种容器:
1 2 3 4 5 6 7 | template <class C1, class C2> C1 move_each(C2&& c2) { return C1(std::make_move_iterator(c2.begin()), std::make_move_iterator(c2.end())); } |
太好了,现在我可以相对有效地从
但是,如果我想从
1 2 3 4 5 6 7 8 9 10 | int main() { std::map<int, std::string> m{{1,"one"}, {2,"two"}, {3,"three"}}; auto v = move_each<std::vector<std::pair<int, std::string>>>(m); for (auto const& p : v) std::cout <<"{" << p.first <<"," << p.second <<"}"; std::cout << ' '; } |
如果
对于此示例,以及在通用编码中无数类似的其他示例,
我和OP一样担心。
std :: move不移动对象,也不保证对象可移动。那为什么叫移动呢?
我认为无法移动可能是以下两种情况之一:
1.移动类型为const。
我们在语言中使用const关键字的原因是我们希望编译器阻止对定义为const的对象进行任何更改。给出斯科特·迈耶斯(Scott Meyers)书中的示例:
1 2 3 4 5 6 7 8 9 | class Annotation { public: explicit Annotation(const std::string text) : value(std::move(text)) //"move" text into value; this code { … } // doesn't do what it seems to! … private: std::string value; }; |
它的字面意思是什么?将const字符串移动到value成员-至少,这是我在阅读说明之前的理解。
如果在调用std :: move()时该语言打算不移动或不保证移动适用,那么在使用单词移动时会产生误导。
如果该语言鼓励人们使用std :: move来提高效率,则必须尽早防止此类陷阱,尤其是对于这种明显的文字矛盾。
我同意人们应该意识到移动一个常数是不可能的,但是这种义务不应该意味着当明显的矛盾发生时编译器可以保持沉默。
2.对象没有移动构造函数
就个人而言,正如克里斯·德鲁(Chris Drew)所说,我认为这是与OP无关的故事
@hvd That seems like a bit of a non-argument to me. Just because OP's suggestion doesn't fix all bugs in the world doesn't necessarily mean it is a bad idea (it probably is, but not for the reason you give). – Chris Drew
我很惊讶没有人提到它的向后兼容性方面。 我相信,