[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