Given the type struct Tp { Tp() = default; Tp(const Tp&) = default; Tp(Tp&&) = default; ~Tp() {} }; The following static asserts fail static_assert(std::is_trivially_copy_constructible<Tp>::value, ""); static_assert(std::is_trivially_move_constructible<Tp>::value, ""); If the destructor is defaulted (or removed, such that it is implicitly generated), the asserts do not trigger. I'm not entirely sure this is a bug, but as far as I can determine from the standard, it is. Relevant text (ยง12.8 [class.copy], paragraph 12): "A copy/move constructor for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if - class X has no virtual functions (10.3) and no virtual base classes (10.1), and - class X has no non-static data members of volatile-qualified type, and - the constructor selected to copy/move each direct base class subobject is trivial, and - for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial; otherwise the copy/move constructor is non-trivial." No mention that a user defined destructor should make either copy or move constructors non-trivial if they otherwise qualify. Additionally, using the old std::has_trivial_copy_constructor (and the built-in __has_trivial_copy it's based on) does not trigger the assert.
Your non-trivial destructor means that is_trivially_xxx_constructible is false, because that trait is based on an expression that creates and destroys an object. That's by design, because simply asking whether the type *has* a trivial constructor doesn't actually help, in typical generic code you want to know if a particular operation is trivial.