16

看看下面的代码:

#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
}

取消注释 g++ 4.7 上的标记行时,编译此代码段失败。产生的错误表明non_copyable无法复制,但我希望它会被移动。

为什么插入std::pair使用统一初始化构造的构造失败但使用构造构造失败std::make_pair?两者都不应该产生可以成功移动到地图中的右值吗?

4

2 回答 2

22

[这是一个完整的重写。我之前的回答与问题无关。]

map有两个相关的insert重载:

  • insert(const value_type& value), 和

  • <template typename P> insert(P&& value).

当您使用简单的 list-initializermap.insert({1, non_copyable()});时,会考虑所有可能的重载。但是只找到第一个(拿走的const value_type&),因为另一个没有意义(没有办法神奇地猜测你打算创建一对)。第一个重载当然不起作用,因为您的元素不可复制。

您可以通过显式创建对来使第二个重载工作,或者使用make_pair,正如您已经描述的那样,或者通过显式命名值类型:

typedef std::map<int, non_copyable> map_type;

map_type m;
m.insert(map_type::value_type({1, non_copyable()}));

现在列表初始化器知道要寻找map_type::value_type构造函数,找到相关的可移动构造函数,结果是一个右值对,它绑定到函数的P&&-overload insert

(另一种选择是emplace()piecewise_constructand一起使用forward_as_tuple,尽管这样会更冗长。)

我想这里的寓意是列表初始化器寻找可行的重载——但他们必须知道要寻找什么!

于 2013-02-17T00:05:14.273 回答
0

除了提供移动(赋值)构造函数的其他答案外,您还可以通过指针存储不可复制的对象,尤其是unique_ptr. unique_ptr将为您处理资源移动。

于 2019-02-26T06:58:34.653 回答