为什么 std::pair 允许使用用户定义的已删除移动构造函数从 class 类型的右值进行初始化?
Why does `std::pair` allow to initialize from an rvalue of class type with a user-defined deleted move constructor?
考虑以下 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;
};
从 我了解到 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 classes 拒绝这种使用。例如,
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
设计得如此特别?
I do know why these behaviors occur ...
不 - 在您的示例中,您正在调用 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.
why std::pair
is designed to be so special?
不是。它恰好有这两个构造函数:
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'd,所以 (0) 仍然存在。那个很好,因为 T&&
绑定到 const T&
,它执行一个副本。
如果我不得不 猜测 为什么 std::pair
有这两个构造函数:class 之前可用转发引用 是发明的,它只公开了 (0)。当 转发引用 被引入语言时,(1) 被添加到 std::pair
。可能是为了保持向后兼容性,构造函数被设计为对 SFINAE 友好。
考虑以下 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;
};
从 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 classes 拒绝这种使用。例如,
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
设计得如此特别?
I do know why these behaviors occur ...
不 - 在您的示例中,您正在调用 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.
why
std::pair
is designed to be so special?
不是。它恰好有这两个构造函数:
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'd,所以 (0) 仍然存在。那个很好,因为 T&&
绑定到 const T&
,它执行一个副本。
如果我不得不 猜测 为什么 std::pair
有这两个构造函数:class 之前可用转发引用 是发明的,它只公开了 (0)。当 转发引用 被引入语言时,(1) 被添加到 std::pair
。可能是为了保持向后兼容性,构造函数被设计为对 SFINAE 友好。