简体   繁体   English

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

[英]Why does `std::pair` allow to initialize from an rvalue of class type with a user-defined deleted move constructor?

Consider the following class: 考虑以下类别:

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;
};

From here I learn that std::pair (as well as std::tuple ) allows to initialize from an Do_not_move rvalue, eg 这里我了解到std::pair (以及std::tuple )允许从Do_not_move rvalue进行初始化,例如

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

However, many other STL classes reject such use. 但是,许多其他STL类拒绝这种使用。 For example, 例如,

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

I do know why these behaviors occur. 我确实知道为什么会发生这些行为。 My question is, why is std::pair designed to be so special? 我的问题是,为什么std::pair设计得如此特别?

I do know why these behaviors occur ... 我知道为什么会发生这些行为...

Nope - in your example you're invoking std::vector::vector(std::initializer_list) and std::set::set(std::initializer_list) . 不,在您的示例中,您正在调用std::vector::vector(std::initializer_list)std::set::set(std::initializer_list) Unfortunately std::initializer_list<T> is basically sugar over a const T[] array - this means that you cannot move from an initializer_list . 不幸的是std::initializer_list<T>基本上是const T[]数组上的糖-这意味着您不能从initializer_list移出。

std::any a{std::move(dnm)}; compiles fine - live example on wandbox.org . 在wandbox.org上编译精细示例


why std::pair is designed to be so special? 为什么std::pair设计得如此特别?

It's not. 不是。 It just happens to have both these constructors: 碰巧同时具有这两个构造函数:

constexpr pair( const T1& x, const T2& y ); // (0)

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

According to cppreference , these constructors are SFINAE-friendly (ie they will not participate in overload resolution if the construction is invalid) . 根据cppreference ,这些构造函数对SFINAE友好(即,如果构造无效,它们将不参与重载解析)

When invoking 调用时

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

first we try to use (1) , which is invalid. 首先,我们尝试使用(1) ,这是无效的。 It gets SFINAE'd out, so that (0) remains. 它被SFINAE淘汰,所以(0)仍然存在。 That one is fine as T&& binds to const T& , and it performs a copy. 这很好,因为T&&绑定到const T& ,并执行复制。

If I had to guess why std::pair has both these constructors: the class was available before forwarding references were invented and it only exposed (0) . 如果我不得不猜测 为什么 std::pair具有这两个构造函数:在创建转发引用之前,该类可用,并且仅暴露(0) When forwarding references were introduced into the language, (1) was added to std::pair . 将转发引用引入该语言时, (1)被添加到std::pair Probably in order to preserve backwards-compatibility, the constructors were designed to be SFINAE-friendly. 可能为了保持向后兼容性,构造函数被设计为对SFINAE友好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么C ++允许类型为std :: map而没有默认构造函数而不是std :: pair? - Why does C++ allow type has no-default constructor for std::map while not std::pair? 用户定义的左值引用类型成员的移动构造函数 - User-defined move constructor for member of lvalue reference type 空的用户定义的移动构造函数 - Empty user-defined move constructor 为什么C ++不允许用户定义的运算符? - Why does C++ not allow user-defined operators? 为什么 std::vector 允许对其包含的类型使用可抛出的移动构造函数? - Why does std::vector allow the use of a throwable move constructor for the type it contains? std :: vector <T>是一个“用户定义的类型”吗? - Is std::vector<T> a `user-defined type`? 提供用户定义的移动构造函数时,Visual Studio 2013不会删除副本构造函数 - Visual Studio 2013 does not delete the copy constructor when a user-defined move constructor is provided 当存在可用的右值构造函数时,为什么从右值调用 class 引用构造函数重载? - Why does the class reference constructor overload get called from an rvalue when there's an rvalue constructor available? 为什么用户定义的move-constructor会禁用隐式复制构造函数? - Why user-defined move-constructor disables the implicit copy-constructor? std ::等于用户定义类的函数 - std::equal function with user-defined class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM