David Brown David Brown - 1 month ago 33
C++ Question

libc++ vs libstdc++ std::is_move_assignable: Which is the most correct?

I'm trying to get a deeper understanding of C++ by reading the C++14 standard along with the source of libc++ and libstdc++. The implementation of various

type_traits
items varies between the two, particularly
is_move_assignable
, and I'm trying to figure out which of them is "more correct."

libc++:

template <class _Tp> struct is_move_assignable
: public is_assignable<typename add_lvalue_reference<_Tp>::type,
const typename add_rvalue_reference<_Tp>::type> {};


libstdc++:

template<typename _Tp, bool = __is_referenceable<_Tp>::value>
struct __is_move_assignable_impl;

template<typename _Tp>
struct __is_move_assignable_impl<_Tp, false>
: public false_type { };

template<typename _Tp>
struct __is_move_assignable_impl<_Tp, true>
: public is_assignable<_Tp&, _Tp&&>
{ };

template<typename _Tp>
struct is_move_assignable
: public __is_move_assignable_impl<_Tp>
{ };


The standard states:


For a referenceable type
T
, the same result as
is_assignable<T&, T&&>::value
, otherwise
false
.


The first thing I noted is that libc++ applies
const
to the second template parameter, which doesn't seem right since the move assignment operator takes a non-const rvalue. libstdc++ also uses
__is_referenceable
, which follows the wording of the standard, but libc++ doesn't. Is that requirement covered by libc++'s use of
add_lvalue_reference
and
add_rvalue_reference
, which both enforce
__is_referenceable
on their own?

I would really appreciate any insight into why each project chose their solutions!

Answer

For anything referenceable, the two implementations do the same thing, since the extraneous const in libc++ is meaningless but also harmless.

(Judging from the diff, it certainly looks like temporary insanity to me :) Seems to be a C&P issue from a (wrong) implementation of is_copy_assignable.)

For anything non-referenceable (i.e., cv void or abominable function types), libstdc++ returns false_type.

In libc++, add_{l,r}value_reference returns it unchanged (this depends on an issue resolution that postdates C++14). Sprinkling a const on top does nothing for AFTs and adds a const for the voidy types.

We then go to is_assignable, which SFINAE-tests the well-formedness of declval<T>() = declval<U>(), for either T == U == some AFT or T == some void type and U = some const-qualified void type. In all cases the expression is ill-formed (in a SFINAE-friendly manner), so we get false_type back.

The two are equivalent.