Broken type deduction for pair of reference_wrappers?

Given two objects, one and two that are references to some pure virtual type MyType, the following code compiles fine:

std::pair<std::reference_wrapper,std::reference_wrapper< MyType >> mypair = std::make_pair(std::ref(one), std::ref(two));
std::swap(mypair.first, mypair.second);

But if I replace the declaration of the pair object type with ‘auto’ as in:

auto mypair = std::make_pair(std::ref(one), std::ref(two));

I get the following errors with std::swap:

GameController.cpp:43:9: error: no matching function for call to ‘swap’
std::swap(mypair.first, mypair.second);
^~~~~~~~~
/usr/include/c++/v1/type_traits:2902:5: note: candidate template ignored: disabled by ‘enable_if’ [with _Tp = MyType]
is_move_constructible<_Tp>::value &&
^
/usr/include/c++/v1/utility:185:1: note: candidate template ignored: failed template argument deduction
swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) NOEXCEPT(__is_nothrow_swappable<_Tp>::value)
^
/usr/include/c++/v1/utility:412:1: note: candidate template ignored: failed template argument deduction
swap(pair<_T1, _T2>& __x, pair<_T1, _T2>& __y)
^
/usr/include/c++/v1/tuple:220:6: note: candidate template ignored: failed template argument deduction
void swap(__tuple_leaf<_Ip, _Hp, _Ep>& __x, __tuple_leaf<_Ip, _Hp, _Ep>& __y)
^
/usr/include/c++/v1/tuple:751:1: note: candidate template ignored: failed template argument deduction
swap(tuple<_Tp…>& __t, tuple<_Tp…>& __u)
^
/usr/include/c++/v1/memory:2440:1: note: candidate template ignored: failed template argument deduction
swap(__compressed_pair<_T1, _T2>& __x, __compressed_pair<_T1, _T2>& __y)
^
/usr/include/c++/v1/memory:2928:1: note: candidate template ignored: failed template argument deduction
swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) _NOEXCEPT {__x.swap(__y);}
^
/usr/include/c++/v1/memory:4862:1: note: candidate template ignored: failed template argument deduction
swap(shared_ptr<_Tp>& __x, shared_ptr<_Tp>& __y) _NOEXCEPT
^
/usr/include/c++/v1/memory:5151:1: note: candidate template ignored: failed template argument deduction
swap(weak_ptr<_Tp>& __x, weak_ptr<_Tp>& __y) _NOEXCEPT
^
/usr/include/c++/v1/array:279:1: note: candidate template ignored: failed template argument deduction
swap(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y)
^
/usr/include/c++/v1/__bit_reference:86:1: note: candidate template ignored: failed template argument deduction
swap(__bit_reference<_Cp> __x, __bit_reference<_Dp> __y) _NOEXCEPT
^
/usr/include/c++/v1/__bit_reference:96:1: note: candidate template ignored: failed template argument deduction
swap(__bit_reference<_Cp> __x, bool& __y) _NOEXCEPT
^
/usr/include/c++/v1/__bit_reference:106:1: note: candidate template ignored: failed template argument deduction
swap(bool& __x, __bit_reference<_Cp> __y) _NOEXCEPT
^
/usr/include/c++/v1/__split_buffer:644:1: note: candidate template ignored: failed template argument deduction
swap(__split_buffer<_Tp, _Allocator>& __x, __split_buffer<_Tp, _Allocator>& __y)
^
/usr/include/c++/v1/vector:3163:1: note: candidate template ignored: failed template argument deduction
swap(vector<_Tp, _Allocator>& __x, vector<_Tp, _Allocator>& __y)
^
/usr/include/c++/v1/functional:1492:1: note: candidate template ignored: failed template argument deduction
swap(function<_Rp(_ArgTypes…)>& __x, function<_Rp(_ArgTypes…)>& __y) _NOEXCEPT
^

Is this a bug?

Given two objects, one and two that are references to some pure virtual type MyType, the following code compiles fine:

std::pair<std::reference_wrapper,std::reference_wrapper< MyType >> mypair = std::make_pair(std::ref(one), std::ref(two));
std::swap(mypair.first, mypair.second);

But if I replace the declaration of the pair object type with ‘auto’ as in:

auto mypair = std::make_pair(std::ref(one), std::ref(two));

mypair’s type is std::pair<MyType&, MyType&>. See the make_pair specification:

template <class T1, class T2> pair<V1, V2> make_pair(T1&& x, T2&& y);

Returns: pair<V1, V2>(std::forward(x), std::forward(y));
where V1 and V2 are determined as follows: Let Ui be decay::type for each Ti. Then each Vi is
X& if Ui equals reference_wrapper, otherwise Vi is Ui.

Then…

I get the following errors with std::swap:

GameController.cpp:43:9: error: no matching function for call to ‘swap’
std::swap(mypair.first, mypair.second);
^~~~~~~~~
/usr/include/c++/v1/type_traits:2902:5: note: candidate template ignored: disabled by ‘enable_if’ [with _Tp = MyType]
is_move_constructible<_Tp>::value &&
^

your values can’t be swapped, because MyType is not move constructible, because it’s an abstract class type. The specification for ‘swap’ says:

Requires: Type T shall be MoveConstructible (Table 20) and MoveAssignable (Table 22).

I’m not sure whether it’s valid to implement this “Requires:” clause as an enable_if, but in any case, I would expect that better diagnostics would be produced if it were instead a static_assert within the definition of swap.

Given two objects, one and two that are references to some pure virtual type MyType, the following code compiles fine:

std::pair<std::reference_wrapper,std::reference_wrapper< MyType >> mypair = std::make_pair(std::ref(one), std::ref(two));
std::swap(mypair.first, mypair.second);

But if I replace the declaration of the pair object type with ‘auto’ as in:

auto mypair = std::make_pair(std::ref(one), std::ref(two));

mypair’s type is std::pair<MyType&, MyType&>. See the make_pair specification:

template <class T1, class T2> pair<V1, V2> make_pair(T1&& x, T2&& y);

Returns: pair<V1, V2>(std::forward(x), std::forward(y));
where V1 and V2 are determined as follows: Let Ui be decay::type for each Ti. Then each Vi is
X& if Ui equals reference_wrapper, otherwise Vi is Ui.

So, I guess that std::pair<MyType&, MyType&> is assignable to std::pair<std::reference_wrapper<MyType&>, std::reference_wrapper<MyType&>> then?

Then…

I get the following errors with std::swap:

GameController.cpp:43:9: error: no matching function for call to ‘swap’
std::swap(mypair.first, mypair.second);
^~~~~~~~~
/usr/include/c++/v1/type_traits:2902:5: note: candidate template ignored: disabled by ‘enable_if’ [with _Tp = MyType]
is_move_constructible<_Tp>::value &&
^

your values can’t be swapped, because MyType is not move constructible, because it’s an abstract class type. The specification for ‘swap’ says:

Requires: Type T shall be MoveConstructible (Table 20) and MoveAssignable (Table 22).

Why does it compile (and it does in fact appear to execute correctly) if mypair is a pair of reference_wrappers then? I don’t think that reference_wrapper is MoveConstrucible and MoveAssignable.

I’m running a pretty recent ToT for both llvm/clang and libcxx on Ubuntu 12.04. Here’s the output from running clang++ -v:

clang version 3.2 (http://llvm.org/git/clang.git b25466e8b33285a13d0303461db37e903ec505c1) (http://llvm.org/git/llvm 913ff09a9acf563ae9719ff223bc117dd66ad6b0)
Target: x86_64-unknown-linux-gnu
Thread model: posix

Given two objects, one and two that are references to some pure virtual
type MyType, the following code compiles fine:

    std::pair<std::reference_wrapper<MyType>,std::reference_wrapper<
MyType >> mypair = std::make_pair(std::ref(one), std::ref(two));
    std::swap(mypair.first, mypair.second);

But if I replace the declaration of the pair object type with 'auto' as
in:

    auto mypair = std::make_pair(std::ref(one), std::ref(two));

mypair's type is std::pair<MyType&, MyType&>. See the make_pair
specification:

template <class T1, class T2> pair<V1, V2> make_pair(T1&& x, T2&& y);

Returns: pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y));
where V1 and V2 are determined as follows: Let Ui be decay<Ti>::type for
each Ti. Then each Vi is
X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.

So, I guess that std::pair<MyType&, MyType&> is assignable to
std::pair<std::reference_wrapper<MyType&>, std::reference_wrapper<MyType&>>
then?

Given that reference_wrapper<T> is implicitly constructible from T&
(though not T&&), yes.

Then...

I get the following errors with std::swap:

GameController.cpp:43:9: error: no matching function for call to 'swap'
        std::swap(mypair.first, mypair.second);
        ^~~~~~~~~
/usr/include/c++/v1/type_traits:2902:5: note: candidate template ignored:
disabled by 'enable_if' [with _Tp = MyType]
    is_move_constructible<_Tp>::value &&
    ^

your values can't be swapped, because MyType is not move constructible,
because it's an abstract class type. The specification for 'swap' says:

Requires: Type T shall be MoveConstructible (Table 20) and MoveAssignable
(Table 22).

Why does it compile (and it does in fact appear to execute correctly) if
mypair is a pair of reference_wrappers then?

Because reference_wrapper is CopyConstructible and CopyAssignable
(hence MoveConstructible and MoveAssignable), which is why it's a good
match for what std::pair needs.

I don't think that
reference_wrapper is MoveConstrucible and MoveAssignable.

Why not?

-- James

Given two objects, one and two that are references to some pure virtual
type MyType, the following code compiles fine:

std::pair<std::reference_wrapper,std::reference_wrapper<
MyType >> mypair = std::make_pair(std::ref(one), std::ref(two));
std::swap(mypair.first, mypair.second);

But if I replace the declaration of the pair object type with ‘auto’ as
in:

auto mypair = std::make_pair(std::ref(one), std::ref(two));

mypair’s type is std::pair<MyType&, MyType&>. See the make_pair
specification:

template <class T1, class T2> pair<V1, V2> make_pair(T1&& x, T2&& y);

Returns: pair<V1, V2>(std::forward(x), std::forward(y));
where V1 and V2 are determined as follows: Let Ui be decay::type for
each Ti. Then each Vi is
X& if Ui equals reference_wrapper, otherwise Vi is Ui.

So, I guess that std::pair<MyType&, MyType&> is assignable to
std::pair<std::reference_wrapper<MyType&>, std::reference_wrapper<MyType&>>
then?

Given that reference_wrapper is implicitly constructible from T&
(though not T&&), yes.

Then…

I get the following errors with std::swap:

GameController.cpp:43:9: error: no matching function for call to ‘swap’
std::swap(mypair.first, mypair.second);
^~~~~~~~~
/usr/include/c++/v1/type_traits:2902:5: note: candidate template ignored:
disabled by ‘enable_if’ [with _Tp = MyType]
is_move_constructible<_Tp>::value &&
^

your values can’t be swapped, because MyType is not move constructible,
because it’s an abstract class type. The specification for ‘swap’ says:

Requires: Type T shall be MoveConstructible (Table 20) and MoveAssignable
(Table 22).

Why does it compile (and it does in fact appear to execute correctly) if
mypair is a pair of reference_wrappers then?

Because reference_wrapper is CopyConstructible and CopyAssignable
(hence MoveConstructible and MoveAssignable), which is why it’s a good
match for what std::pair needs.

I don’t think that
reference_wrapper is MoveConstrucible and MoveAssignable.

Why not?

The following web page mentions CopyConstructible and CopyAssignable, but not the move equivalents (granted, it is not authoritative). http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper

Why would copyable imply movable? You can definitely have types that are copyable but not movable as they can be deleted independently.

The standard specifies the requirements for CopyConstructible and
CopyAssignable as refinements of MoveConstructible and MoveAssignable
respectively (see tables 20-23).

-- James

your values can’t be swapped, because MyType is not move constructible,
because it’s an abstract class type. The specification for ‘swap’ says:

Requires: Type T shall be MoveConstructible (Table 20) and
MoveAssignable
(Table 22).

Why does it compile (and it does in fact appear to execute correctly) if
mypair is a pair of reference_wrappers then?

Because reference_wrapper is CopyConstructible and CopyAssignable
(hence MoveConstructible and MoveAssignable), which is why it’s a good
match for what std::pair needs.

I don’t think that
reference_wrapper is MoveConstrucible and MoveAssignable.

Why not?

The following web page mentions CopyConstructible and CopyAssignable, but
not the move equivalents (granted, it is not authoritative).
http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper

Why would copyable imply movable? You can definitely have types that are
copyable but not movable as they can be deleted independently.

The standard specifies the requirements for CopyConstructible and
CopyAssignable as refinements of MoveConstructible and MoveAssignable
respectively (see tables 20-23).

20.8.3 Class template reference_wrapper [refwrap]

namespace std {
template class reference_wrapper {
public :

// construct/copy/destroy
reference_wrapper(T&) noexcept;
reference_wrapper(T&&) = delete; // do not bind to temporary objects
reference_wrapper(const reference_wrapper& x) noexcept;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept;

};
}

Clearly that thing cannot be move constructed. Maybe we are talking past each other at this point. I feel that I still don’t quite understand how a type could be considered MoveConstructible, yet have a deleted move constructor, but I’m willing to chalk that up to language-lawyer-ese.

Thanks for your help. I guess it’s back to specifying the full type instead of using the shiny ‘auto’ keyword.

>>

<SNIP>

>> >>
>> >> your values can't be swapped, because MyType is not move
>> >> constructible,
>> >> because it's an abstract class type. The specification for 'swap'
>> >> says:
>> >>
>> >> Requires: Type T shall be MoveConstructible (Table 20) and
>> >> MoveAssignable
>> >> (Table 22).
>> >>
>> > Why does it compile (and it does in fact appear to execute correctly)
>> > if
>> > mypair is a pair of reference_wrappers then?
>>
>> Because reference_wrapper is CopyConstructible and CopyAssignable
>> (hence MoveConstructible and MoveAssignable), which is why it's a good
>> match for what std::pair needs.

In hindsight: The standard makes this claim, but I think it's wrong.

>> > I don't think that
>> > reference_wrapper is MoveConstrucible and MoveAssignable.
>>
>> Why not?
>
> The following web page mentions CopyConstructible and CopyAssignable,
> but
> not the move equivalents (granted, it is not authoritative).
> std::reference_wrapper - cppreference.com
>
> Why would copyable imply movable? You can definitely have types that are
> copyable but not movable as they can be deleted independently.

The standard specifies the requirements for CopyConstructible and
CopyAssignable as refinements of MoveConstructible and MoveAssignable
respectively (see tables 20-23).

20.8.3 Class template reference_wrapper [refwrap]

namespace std {
  template <class T> class reference_wrapper {
  public :
...
    // construct/copy/destroy
    reference_wrapper(T&) noexcept;
    reference_wrapper(T&&) = delete; // do not bind to temporary objects
    reference_wrapper(const reference_wrapper<T>& x) noexcept;
    // assignment
    reference_wrapper& operator=(const reference_wrapper<T>& x) noexcept;
...
  };
}

Clearly that thing cannot be move constructed. Maybe we are talking past
each other at this point. I feel that I still don't quite understand how a
type could be considered MoveConstructible, yet have a deleted move
constructor, but I'm willing to chalk that up to language-lawyer-ese.

You have a point, and I think that this is a defect in the standard.
reference_wrapper is documented as being
CopyConstructible/CopyAssignable, but it is not. CopyConstructible
implies MoveConstructible which implies that construction from an
rvalue is permitted, but reference_wrapper disables that, so it's not
MoveConstructible and hence no longer meets the CopyConstructible
requirements (though its predecessor did in a world before move
semantics).

Thanks for your help. I guess it's back to specifying the full type instead
of using the shiny 'auto' keyword.

Yes, if the special-casing of reference wrapper is getting in your way
then that's the way around it.

-- James

>>

<SNIP>

>> >>
>> >> your values can't be swapped, because MyType is not move
>> >> constructible,
>> >> because it's an abstract class type. The specification for 'swap'
>> >> says:
>> >>
>> >> Requires: Type T shall be MoveConstructible (Table 20) and
>> >> MoveAssignable
>> >> (Table 22).
>> >>
>> > Why does it compile (and it does in fact appear to execute correctly)
>> > if
>> > mypair is a pair of reference_wrappers then?
>>
>> Because reference_wrapper is CopyConstructible and CopyAssignable
>> (hence MoveConstructible and MoveAssignable), which is why it's a good
>> match for what std::pair needs.

In hindsight: The standard makes this claim, but I think it's wrong.

But I was wrong about being wrong. Which still means I was wrong, but
about the wrong thing.

>> > I don't think that
>> > reference_wrapper is MoveConstrucible and MoveAssignable.
>>
>> Why not?
>
> The following web page mentions CopyConstructible and CopyAssignable,
> but
> not the move equivalents (granted, it is not authoritative).
> std::reference_wrapper - cppreference.com
>
> Why would copyable imply movable? You can definitely have types that are
> copyable but not movable as they can be deleted independently.

The standard specifies the requirements for CopyConstructible and
CopyAssignable as refinements of MoveConstructible and MoveAssignable
respectively (see tables 20-23).

20.8.3 Class template reference_wrapper [refwrap]

namespace std {
  template <class T> class reference_wrapper {
  public :
...
    // construct/copy/destroy
    reference_wrapper(T&) noexcept;
    reference_wrapper(T&&) = delete; // do not bind to temporary objects
    reference_wrapper(const reference_wrapper<T>& x) noexcept;
    // assignment
    reference_wrapper& operator=(const reference_wrapper<T>& x) noexcept;
...
  };
}

Clearly that thing cannot be move constructed. Maybe we are talking past
each other at this point. I feel that I still don't quite understand how a
type could be considered MoveConstructible, yet have a deleted move
constructor, but I'm willing to chalk that up to language-lawyer-ese.

As others have flagged, it's only the constructor from T&& to
reference_wrapper<T> that's deleted, not the move constructor
reference_wrapper<T>::reference_wrapper(reference_wrapper<T>&&).
reference_wrapper<T> can be copied, including from temporaries, and
all is good.