__has_trivial_assign and __has_trivial_copy are obsolete

I believe explicitly defaulted special members obsolete __has_trivial_assign and __has_trivial_copy.

Consider:

class A
{
    int i;
public:
    A(const A&);
    A(A&) = default;
};

With explicitly defaulted functions, I believe the following is the complete list of special members that may be trivial:

// default construction
A()

// copy construction
A(const A&)
A(A&)

// move construction
A(A&&)

// copy assignment
A& operator=(const A&)
A& operator=(const A&) &
A& operator=(const A&) &&
A& operator=(A&)
A& operator=(A&) &
A& operator=(A&) &&

// move assignment
A& operator=(A&&)
A& operator=(A&&) &
A& operator=(A&&) &&

// destruction
~A()

std::is_trivially_constructible<T, Args...> can ask if either of the copy constructors are trivial.

And std::is_trivially_assignable<T, U> can ask if any of those 9 assignment operators are trivial. __has_trivial_assign(T) and __has_trivial_copy(T) are not up to this task.

Recommendation:

Given: template <class T> T&& declval() noexcept;

__is_trivial_constructor(T, U);

Effects: Returns true if:

   T t(declval<U>());

is well-formed and calls a trivial constructor.

This would be used like so:

A(const A&) // __is_trivial_constructor(A, const A&)
A(A&) // __is_trivial_constructor(A, A&)
A(A&&) // __is_trivial_constructor(A, A&&) or __is_trivial_constructor(A, A)

__is_trivial_assign(T, U);

Effects: Returns true if:

   declval<T>() = declval<U>();

is well-formed and calls a trivial assignment operator.

__is_trivial_assign(T, U) would be used like so:

A& operator=(const A&) // __is_trivial_assign(A, const A&) or __is_trivial_assign(A&, const A&)
A& operator=(const A&) & // __is_trivial_assign(A&, const A&)
A& operator=(const A&) && // __is_trivial_assign(A, const A&) or __is_trivial_assign(A&&, const A&)
A& operator=(A&) // __is_trivial_assign(A, A&) or __is_trivial_assign(A&, A&)
A& operator=(A&) & // __is_trivial_assign(A&, A&)
A& operator=(A&) && // __is_trivial_assign(A&&, A&) or __is_trivial_assign(A, A&)

// move assignment
A& operator=(A&&) // __is_trivial_assign(A, A&&) or __is_trivial_assign(A, A) or ...
A& operator=(A&&) & // __is_trivial_assign(A&, A&&) or __is_trivial_assign(A&, A)
A& operator=(A&&) && // __is_trivial_assign(A, A&&) or __is_trivial_assign(A, A) or ...

Note that we're dealing with expressions which need to go through overload resolution to determine the proper constructor or assignment operator, not with specific signatures. So __is_trivial_assign(A, A) should return true for a class A with a trivial copy assignment operator=(const A&) and no move assignment operator.

Also note that the compiler doesn't need to deal with arbitrary types T and U. The library can handle outrageous combinations like T=void U=int. The compiler can assume that T and U are closely related as demonstrated in these examples. In fact the compiler can even assume that the expressions are valid (I know how to check that without further compiler support). I just don't know how to check if the expressions are trivial.

Finally, I need one more trait in this area:

   __is_trivially_copyable(T)

Effects: Returns true if T is a trivially-copyable type.

T is a trivially-copyable type if T:

* has no non-trivial copy constructors.
* has no non-trivial move constructors.
* has no non-trivial copy assignment operators.
* has no non-trivial move assignment operators.
* has a trivial destructor.

This differs from __is_trivial(T) in that a trivial type is trivially-copyable *and* has a trivial default constructor.

C++11 <type_traits> appears to be unimplementable without support along these lines.

Howard

This one is very easy; I'll implement it right away. If someone else isn't poking around the area, I'll get to the others relatively soon.

Sean

That's great, thanks Sean!

I've updated the type_traits design table with the current status:

http://libcxx.llvm.org/type_traits_design.html

Howard