C++ 使用不可复制(但可移动)键移动地图分配时出错
为什么这不起作用:C++ 使用不可复制(但可移动)键移动地图分配时出错,c++,c++14,move-semantics,stdmap,C++,C++14,Move Semantics,Stdmap,为什么这不起作用: #include <memory> #include <map> std::map<std::unique_ptr<char>, std::unique_ptr<int>> foo(); std::map<std::unique_ptr<char>, std::unique_ptr<int>> barmap; int main(){ barmap=foo(); retu
#include <memory>
#include <map>
std::map<std::unique_ptr<char>, std::unique_ptr<int>> foo();
std::map<std::unique_ptr<char>, std::unique_ptr<int>> barmap;
int main(){
barmap=foo();
return 0;
}
要查看的整个错误消息
在我看来,std::pair
的默认移动构造函数试图使用std::unique\u ptr
的副本构造函数。我假设map assignment操作符使用将新映射内容的分配移到旧映射内容上,而std::swap
不能这样做,因为它需要保持旧内容不变,所以它只交换内部数据指针,从而避免了问题
移动分配的必要性(至少能够)可能来自于C++11中的allocator\u traits::propagate\u on\u container\u move\u assignment
,但我的印象是,在C++14中,整个事情是固定的。我不知道为什么STL会选择移动赋值元素,而不是在move assignment操作符中在容器之间交换数据指针
所有这些都不能解释为什么moved map中包含的对的移动赋值失败——当然不应该
顺便说一句:g++-v
:
gcc version 4.9.2 (Ubuntu 4.9.2-0ubuntu1~14.04)
允许要求将移动分配到地图的值\u类型
理由:
从§23.4.4.1开始
对于map
而言,key类型为key,value类型为pair
§23.2.3
5对于set和multiset,值类型与键类型相同。对于map和multimap,它等于对
7关联容器满足分配器感知容器(23.2.1)的所有要求,但
对于map和multimap,表95中对value_类型的要求适用于key_类型
和映射的U类型。[注:例如,在某些情况下,需要输入key_type和mapped_type
即使关联的值\u类型pair不是
可转让副本-尾注]
从表95中可以看出:
表达方式:
a=rv
返回类型:
X&
操作语义:
移动的所有现有元素要么分配给要么被销毁
断言/注释前置/后置条件:
a应等于分配前rv的值
复杂性:
线性的
因此,您需要提供一个const-Key&&move分配,以使其可移植
像这样:
#include <memory>
#include <map>
struct key {
key(key&&);
key(const key&&);
key& operator=(key&&);
key& operator=(const key&&);
};
bool operator<(const key& l, const key& r);
struct value {
};
using map_type = std::map<key, value>;
map_type foo();
map_type foo2();
int main(){
auto barmap=foo();
barmap = foo2();
return 0;
}
#包括
#包括
结构键{
键(键&&);
键(常数键和&);
键和运算符=(键和);
键和运算符=(常数键和);
};
bool操作符我认为这是libstdc++中的一个bug实现质量问题。如果我们查看容器要求表(现在),其中一个要求是:
a = rv
其中,a
是X
(容器类)类型的值,rv
表示X
类型的非常量值。操作语义描述为:
a
的所有现有元素要么被指定移动,要么被销毁
声明如下:
map
满足集装箱的所有要求
其中一个要求是移动分配。显然,libstdc++的方法是移动赋值元素,即使键
是不可复制的(这将使对
不可移动-注意这里只与键
的不可复制性相关)。但并没有强制要求搬家,这只是一种选择。请注意,该代码可以用libc++很好地编译对我来说,这看起来是C++规范中规范的根本性失败。规范在“不要重复你自己”中走得太远,以至于变得不可读和模棱两可(imho)
如果您进一步阅读表分配器感知容器要求,同一行显示(对于a=rv
):
需要:Ifallocator\u
可以看出,绝对不需要对键类型
或值类型
提出任何要求
我们是否应该人为地对这些类型提出要求
那有什么用呢?它会帮助还是伤害std::map
的客户
我个人的观点是,对不需要的客户类型提出要求只会让客户感到沮丧
<>我也相信C++标准的当前规范是如此复杂,以至于专家们也不能就规范所说的达成一致。这并不是因为专家是白痴。这是因为制定一个正确、明确的规范(在这种规模上)确实是一个非常困难的问题
最后,我认为目的是(或应该是)当规范冲突发生时,支持分配器的容器需求取代容器需求
最后一个复杂问题:在C++11中:
allocator_traits<allocator<T>>::propagate_on_container_move_assignment{} is false_type
allocator\u traits做了这个改变。嗯,我想你已经改变了。您说第二个代码块可以工作,但随后您继续显示它的编译器错误。在第一个代码块中也没有tmp
。在第二个示例中也没有定义MyPtr
。@NathanOliver对不起,我的错。这两个代码实际上都有错误-现已修复。ideone.com上的错误实际上与第一个块代码略有不同,但相当于第一个块代码。这两个代码显然都无法编译,因为没有定义foo,但如果我甚至尝试将其链接到这里,那么我的问题就已经解决了。我省略了foo
definition,因为我不想给编译器太多优化的机会来解决这个问题。我通读了C++14中的23.2.1,在我看来第一个代码是否可以工作,但这是一个相当复杂的部分,所以可能我忽略了一些代码对映射有价值的引用,这份复印作业怎么样?从foo
生成的值是右值,因此应该使用移动赋值。使用自动导致这是移动建设-我知道它的工作,但什么
#include <memory>
#include <map>
struct key {
key(key&&);
key(const key&&);
key& operator=(key&&);
key& operator=(const key&&);
};
bool operator<(const key& l, const key& r);
struct value {
};
using map_type = std::map<key, value>;
map_type foo();
map_type foo2();
int main(){
auto barmap=foo();
barmap = foo2();
return 0;
}
a = rv
allocator_traits<allocator<T>>::propagate_on_container_move_assignment{} is false_type
allocator_traits<allocator<T>>::propagate_on_container_move_assignment{} is true_type