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

为什么“std::pair”允许使用用户定义的删除移动构造函数从类类型的rvalue初始化?

  •  1
  • xskxzr  · 技术社区  · 7 年前

    考虑以下类别:

    struct Do_not_move {
        Do_not_move() = default;
        Do_not_move(const Do_not_move&) = default;
        Do_not_move(Do_not_move&&) = delete;
    private:
        int dummy;
    };
    

    从…起 here 我知道了 std::pair (以及 std::tuple )允许从初始化 Do_not_move R值,例如。

    Do_not_move dnm;
    std::pair<int, Do_not_move> p(0, std::move(dnm)); // work well
    

    然而,许多其他STL类拒绝这样的使用。例如

    Do_not_move dnm;
    std::vector<Do_not_move> v{std::move(dnm)}; // error
    std::set<Do_not_move> s{std::move(dnm)};    // error
    std::any a{std::move(dnm)};                 // error
    

    我确实知道为什么会发生这些行为。我的问题是,为什么 std::对 设计得如此特别?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Vittorio Romeo    7 年前

    我知道为什么会发生这些行为。。。

    不,在您的示例中,您正在调用 std::vector::vector(std::initializer_list) std::set::set(std::initializer_list) . 不幸地 std::initializer_list<T> 基本上是糖分 const T[] 数组-这意味着您不能从 initializer_list .

    std::any a{std::move(dnm)}; 编译良好- live example on wandbox.org .


    为什么? std::pair 设计得如此特别?

    事实并非如此。恰好有两个构造函数:

    constexpr pair( const T1& x, const T2& y ); // (0)
    
    template <typename U1, typename U2>
    constexpr pair( U1&& x, U2&& y );  // (1)
    

    根据 cppreference ,这些施工人员对SFINAE很友好 (即如果施工无效,他们将不参与超载解决) .

    调用时

    std::pair<int, Do_not_move> p(0, std::move(dnm));
    

    首先,我们尝试使用 (1) ,无效。它被杀了,所以 (0) 残余那一个很好 T&& 绑定到 const T& ,并执行复制。

    如果我必须的话 猜测 为什么? std::对 具有这两个构造函数:该类以前可用 转发引用 都是发明出来的 (0) . 什么时候 转发引用 被引入语言, (1) 已添加到 std::对 . 可能是为了保持向后兼容性,构造函数被设计为SFINAE友好的。