我试图将仅移动类型插入地图中。我有以下代码:
#include <map>
class Moveable
{
public:
Moveable() = default;
Moveable(const Moveable&) = delete;
Moveable(Moveable&&) = default;
Moveable& operator=(const Moveable&) = delete;
Moveable& operator=(Moveable&&) = default;
};
int main() {
std::map<int,Moveable> my_map;
Moveable my_moveable_1, my_moveable_2, my_moveable_3;
my_map.insert(std::pair<int,Moveable>{1, std::move(my_moveable_1)}); // (1)
my_map.insert(std::make_pair(2, std::move(my_moveable_2))); // (2)
my_map.insert({3, std::move(my_moveable_3)}); // (3)
return 0;
}
使用VisualC ++第1,2和3行编译会发生什么。在clang和gcc中,只有1和2编译,第3行给出错误(使用已删除的复制构造函数)。
问题:哪种编译器是正确的,为什么?
在此处试试:rextester
答案 0 :(得分:1)
std::map::insert
有(以及其他)以下重载:
// 1.
std::pair<iterator,bool> insert( const value_type& value );
// 2. (since C++11)
template< class P >
std::pair<iterator,bool> insert( P&& value );
// 3. (since C++17)
std::pair<iterator,bool> insert( value_type&& value );
第一个过载显然不能与仅移动类型一起使用。但是第二个重载虽然在C ++ 11中可用,但在这里不适用于大括号,因为大括号不会出现模板参数推断(至少在C ++ 11中,不确定以后的标准)。
您对insert
的第一次和第二次调用都在C ++ 11或更高版本中工作,因为编译器知道类型,但第三次失败。
C ++ 17增加了另一个与大括号和仅移动类型一起使用的重载。现在,为什么它适用于某些编译器而不是其他编译器,这很可能是由于C ++ 17支持或编译器标志的差异造成的。
更新:只是为了让它显而易见:使用大括号我的意思是仅使用大括号(聚合初始化或隐式构造函数调用),即insert({k,v})
,而不是insert(pair<K,V>{k,v})
。在后一种情况下,类型是已知的,即使在C ++ 11中也可以选择模板化的重载。
答案 1 :(得分:0)
我用g ++ 7.3测试了你的代码,它编译时没有错误!
使用clang ++ 5.0.1时无法编译。
我认为您正在使用c ++ 20的一个功能,因此尚未在所有编译器上提供支持。