代码之家  ›  专栏  ›  技术社区  ›  unignore

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

  •  4
  • unignore  · 技术社区  · 6 年前

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

    #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

    2 回复  |  直到 6 年前
        1
  •  1
  •   Arne Vogel    6 年前

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

        2
  •  0
  •   Giggi    6 年前

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

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

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