将使用列表初始化的pair插入到std::map中

4

我正在尝试将一个只能移动的类型插入到一个映射中。我有以下代码:

#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行能够成功编译,第三行会出现错误(使用了已删除的复制构造函数)。
问题:哪个编译器是正确的?为什么?
请点击此处进行测试: rextester

1
这并不回答你的问题,但在这里使用 my_map.emplace(3, std::move(my_moveable_3)); 更有意义。 - super
2个回答

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中选择模板重载。

那么我从中得到的是它应该可以编译,所以 VC 是正确的,对吗? - unignore
1
是的,但请注意,您链接的网站上的GCC(5.4)和Clang(3.8)版本均在C++17之前发布。正如@Giggi所指出的那样,GCC 7.3接受该代码。不幸的是,我尝试了GCC 6.3和Clang 6.0预发布版,但都没有成功。(我不确定我有哪个libc ++版本。) - Arne Vogel
自从C++11以来,关于函数参数推导并没有什么改变:只是在花括号中没有类型可供推导。 - Davis Herring
@DavisHerring 最后一部分不是真的(这正是整个问题的重点):从花括号初始化列表进行模板参数推导仍然不起作用。从重载集合中推导出一个已知类型(这里是value_type &&)是完全不同的故事。 - Arne Vogel
@ArneVogel:没有任何“推断”不是模板参数推断——为已知类型(对于每个候选项)确定如何解释大括号只是重载决议的一部分。我所说的“没有类型”是指没有参数类型可以推导出来,抱歉那不够精确。 - Davis Herring

0

我已经使用g++ 7.3测试了您的代码,它可以编译而没有错误!

但是,使用clang++ 5.0.1编译时会出现错误。

我认为您正在使用c++20的某个特性,因此并非所有编译器都支持该特性。


2
那将是哪个C++20的特性? - David G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接