[英]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.