C++ 将列表初始化对插入std::map

C++ 将列表初始化对插入std::map,c++,insert,stdmap,std-pair,list-initialization,C++,Insert,Stdmap,Std Pair,List Initialization,我正在尝试将仅移动类型插入地图。我有以下代码: #include <map> class Moveable { public: Moveable() = default; Moveable(const Moveable&) = delete; Moveable(Moveable&&) = default; Moveable& operator=(const Moveable&) = delete; Mo

我正在尝试将仅移动类型插入地图。我有以下代码:

#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;
}
#包括
类可移动
{
公众:
Moveable()=默认值;
可移动(常量可移动&)=删除;
可移动(可移动&&)=默认值;
可移动&运算符=(常量可移动&)=删除;
可移动和运算符=(可移动和=)=默认值;
};
int main(){
映射我的地图;
可移动我的可移动1,我的可移动2,我的可移动3;
my_map.insert(std::pair{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)
返回0;
}
使用VisualC++第1、2和3行进行编译。在clang和gcc中,只有1和2编译,第3行给出错误(使用已删除的复制构造函数)

问题:哪个编译器是正确的,为什么


在这里试试:

我已经用g++7.3测试了你的代码,它编译时没有错误

使用clang++5.0.1时,它不会编译


我认为您正在使用c++20的一项功能,因此尚未在所有编译器上准备好支持。

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 );
//1。
标准::对插入(常数值\类型和值);
// 2. (从C++11开始)
模板
标准::对插入(P和值);
// 3. (从C++17开始)
标准::成对插入(值\类型和值);
第一个重载显然不能用于仅移动类型。但是,第二个重载虽然在C++11中可用,但在这里不适用于大括号,因为模板参数推导不会在大括号中发生(至少在C++11中是这样,不确定以后的标准)

insert
的第一次和第二次调用都可以在C++11或更高版本中工作,因为编译器知道类型,但第三次调用失败

C++17添加了另一个重载,可用于大括号和仅移动类型。至于为什么它能与某些编译器一起工作而不能与其他编译器一起工作,这很可能是由于C++17支持级别或编译器标志的不同


更新:只是让它痛苦地显而易见:对于使用大括号,我的意思是只使用大括号(聚合初始化或隐式构造函数调用),即
insert({k,v})
,而不是
insert(pair{k,v})
。在后一种情况下,类型是已知的,即使在C++11中也可以选择模板重载。

这并不能回答您的问题,但使用
my_map.emplace(3,std::move(my_moveable_3))更有意义这里。这是什么C++20功能?所以我从中得到的是它应该编译,所以VC是正确的,对吗?是的,但请注意,你链接到的网站上的GCC(5.4)和Clang(3.8)版本都是在C++17之前发布的。正如@Giggi所指出的,GCC 7.3接受该代码。不幸的是,我尝试了GCC6.3和Clang6.0预发行版,但都没有成功。(我不确定我的libc++版本是什么。)自C++11以来,函数参数推断没有任何变化:只是没有可以从大括号中推断的类型。@davishering最后一部分是不正确的(这就是全部要点):从大括号init列表中推断模板参数仍然不起作用。从重载集中推断已知类型(此处:
value\u type&&
)是一个完全不同的故事。@ArneVogel:没有任何“推断”不是模板参数推断确定如何解释已知类型的大括号(对于每个候选类型)只是重载解析的一部分。我所说的“无类型”指的是无参数类型,不能从中推断,抱歉,这不准确。