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

3

17

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!

Yardley answered 11/10, 2016 at 20:29 Comment(3)
For anything referenceable, that const is meaningless (cv-qualifiers are ignored when applied to a reference type).Riffe
@Riffe Thanks! Any idea why the authors might have added const, then?Yardley
It's not often you can get a response time like this: github.com/llvm-mirror/libcxx/commit/… :-)Propitiate
R
7

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.

Riffe answered 11/10, 2016 at 21:49 Comment(0)
P
8

Thanks! Any idea why the authors might have added const, then?

My best guess is temporary (hopefully) insanity:

https://github.com/llvm-mirror/libcxx/commit/6063ec176d5056683d6ddd310c2e3a8f1c7e1b46#diff-48f5ee43879b5ad38888f0a6ead10113R1245

;-)

I removed the const and ran the current unit tests and nothing failed.

Propitiate answered 11/10, 2016 at 21:6 Comment(0)
R
7

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.

Riffe answered 11/10, 2016 at 21:49 Comment(0)
H
5

__is_referenceable is a non-standard, internal libstdc++ routine. (That doesn't mean it's bad, just that I wouldn't expect libc++ to use it). Also, the "is referenceable" concept came along much later than is_move_assignable.

The __is_referenceable helps when dealing with "abominable functions"; things like int (*) (double) &&.

Looks like I need to write more tests :-)

Haunted answered 11/10, 2016 at 21:16 Comment(2)
libc++ actually does define __is_referenceable for the implementation of add_lvalue_reference and add_rvalue_reference, but I wasn't sure if applying __is_referenceable "implicitly" through the use of add_rvalue_reference would always yield the same behavior as libstdc++'s explicit use of __is_referenceable. Sorry if that doesn't make sense.Yardley
That (*) shouldn't be there :)Riffe

© 2022 - 2024 — McMap. All rights reserved.