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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么`std :: pair`允许使用用户定义的删除移动构造函数从类类型的右值初始化?相关的知识,希望对你有一定的参考价值。

考虑以下课程:

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右值初始化,例如

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::pair设计得如此特别?

答案

我知道为什么会出现这些行为......

不 - 在你的例子中,你正在调用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),这是无效的。它得到了SFINAE,因此(0)仍然存在。那个很好,因为T&&const T&绑定,它执行副本。

如果我不得不猜测为什么std::pair有这两个构造函数:在转发引用被发明之前该类是可用的并且它只暴露(0)。当转发引用被引入语言时,(1)被添加到std::pair。可能为了保持向后兼容性,构造函数被设计为SFINAE友好的。

以上是关于为什么`std :: pair`允许使用用户定义的删除移动构造函数从类类型的右值初始化?的主要内容,如果未能解决你的问题,请参考以下文章

std::multimap

C++ std::pair<,> 是什么怎么用

错误 C2079 'std::pair<Dummy<int>,Dummy<int>>::first' 使用未定义的类 'Dummy<int>'

什么是 C++ std::pair 的 C# 模拟?

C++ 中的 std::pair 和 std::tuple

C++ 中的 std::pair 和 std::tuple