问题
Have a look at the following code:
#include <utility>
#include <map>
// non-copyable but movable
struct non_copyable {
non_copyable() = default;
non_copyable(non_copyable&&) = default;
non_copyable& operator=(non_copyable&&) = default;
// you shall not copy
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
int main() {
std::map<int, non_copyable> map;
//map.insert({ 1, non_copyable() }); < FAILS
map.insert(std::make_pair(1, non_copyable()));
// ^ same and works
}
Compiling this snippet fails when uncommenting the marked line on g++ 4.7. The error produced indicates that non_copyable
can't be copied, but I expected it to be moved.
Why does inserting a std::pair
constructed using uniform initialization fail but not one constructed using std::make_pair
? Aren't both supposed to produce rvalues which can be successfully moved into the map?
回答1:
[This is a complete rewrite. My earlier answer had nothing to do with the problem.]
The map
has two relevant insert
overloads:
insert(const value_type& value)
, and<template typename P> insert(P&& value)
.
When you use the simple list-initializer map.insert({1, non_copyable()});
, all possible overloads are considered. But only the first one (the one taking const value_type&
) is found, since the other doesn't make sense (there's no way to magically guess that you meant to create a pair). The first overload doesn't work of course since your element isn't copyable.
You can make the second overload work by creating the pair explicitly, either with make_pair
, as you already described, or by naming the value type explicitly:
typedef std::map<int, non_copyable> map_type;
map_type m;
m.insert(map_type::value_type({1, non_copyable()}));
Now the list-initializer knows to look for map_type::value_type
constructors, finds the relevant movable one, and the result is an rvalue pair which binds to the P&&
-overload of the insert
function.
(Another option is to use emplace()
with piecewise_construct
and forward_as_tuple
, though that would get a lot more verbose.)
I suppose the moral here is that list-initializers look for viable overloads – but they have to know what to look for!
回答2:
Besides other answers of providing move (assignment) constructor, you could also store the non-copyable object through pointer, especially unique_ptr
. unique_ptr
will handle resource movement for you.
来源:https://stackoverflow.com/questions/14916474/stdmapinsert-using-non-copyable-objects-and-uniform-initialization