Broken type deduction for pair of reference_wrappers?

The deleted constructor is from T&&, so it is not a move constructor, but just a regular one.

The MoveConstructible and MoveAssignable requirements are designed to allow the standard containers to be specified. Thus, having move as a superset of copy makes sense and allows for less verbosity without loosing too much clarity. It is also the case that attempting to move something that only has copy operations will work, since copy constructors accept a superset of what move constructors accept.

The standard is right, it’s just a matter of communication and understanding. Though I’m not sure if I’ve explained clearly enough yet.

Ah, yes, I jumped the gun when I saw && and delete in the same signature. I believe you are correct and it makes more sense now.

So there are several misconceptions going on here. Richard noted earlier today that this really doesn't have anything to do with reference_wrapper:

#include <functional>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <cxxabi.h>

template <typename T>
std::unique_ptr<char, void(*)(void*)>
type_name(const T&)
{
    return std::unique_ptr<char, void(*)(void*)>
           (
                __cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr,
                                           nullptr, nullptr),
                std::free
           );
}

struct MyType {virtual void foo() = 0;};
struct X : MyType {virtual void foo() {}};

int main()
{
    X a, b;
    MyType& one = a;
    MyType& two = b;
    auto mypair = std::make_pair(std::ref(one), std::ref(two));
    std::cout << type_name(mypair).get() << '\n';
}

std::__1::pair<MyType&, MyType&>

I.e. make_pair converts reference_wrapper<T> into T&.

So the question now becomes: Can you use the generic std::swap on two lvalues of MyType. It is easy to see if you try out a simplified home-made swap:

#include <functional>
#include <algorithm>
#include <cstdlib>
#include <iostream>

template <class T>
void
my_swap(T& x, T& y)
{
    T tmp(std::move(x));
    x = std::move(y);
    y = std::move(tmp);
}

struct MyType {virtual void foo() = 0;};
struct X : MyType {virtual void foo() {}};

int main()
{
    X a, b;
    MyType& one = a;
    MyType& two = b;
    auto mypair = std::make_pair(std::ref(one), std::ref(two));
    my_swap(mypair.first, mypair.second);
}

test.cpp:64:7: error: variable type 'MyType' is an abstract class
    T tmp(std::move(x));
      ^
test.cpp:82:5: note: in instantiation of function template specialization 'my_swap<MyType>' requested here
    my_swap(mypair.first, mypair.second);
    ^
1 error generated.

Or perhaps more concisely:

    static_assert(std::is_move_constructible<MyType>::value, "MyType is not move constructible");

Fires:

test.cpp:79:5: error: static_assert failed "MyType is not move constructible"
    static_assert(std::is_move_constructible<MyType>::value, "MyType is not move constructible");
    ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Otoh, std::reference_wrapper<MyType> is both MoveConstructible and MoveAssignable. And that's why when you assign the pair<MyType&, MyType&> returned from make_pair back into a std::pair<std::reference_wrapper<MyType>,std::reference_wrapper< MyType >>, it works.

Howard