简体   繁体   中英

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

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. 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?

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) . Unfortunately std::initializer_list<T> is basically sugar over a const T[] array - this means that you cannot move from an initializer_list .

std::any a{std::move(dnm)}; compiles fine - live example on wandbox.org .


why std::pair is designed to be so special?

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) .

When invoking

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

first we try to use (1) , which is invalid. It gets SFINAE'd out, so that (0) remains. That one is fine as T&& binds to const T& , and it performs a copy.

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) . When forwarding references were introduced into the language, (1) was added to std::pair . Probably in order to preserve backwards-compatibility, the constructors were designed to be SFINAE-friendly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM