[cfe-dev] __has_trivial_assign and __has_trivial_copy are obsolete

Howard Hinnant hhinnant at apple.com
Thu May 12 15:28:49 PDT 2011


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;
};

A::A(const A&) is not trivial.  But A::A(A&) *is* trivial.  What will __has_trivial_copy(A) return?

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




More information about the cfe-dev mailing list