Motivation

Virtual destructors are currently non-trivial, by definition ([class.dtor] p6). This is overly
strict.

The informal meaning of trivial here is "has no observable effects and can be omitted",
but there are two contexts in which this question can be asked. First, whether the destructor
itself has no observable effects; second, whether an expression invoking the destructor (such as
p->~X()) has no observable effects.

In the first case, it doesn’t matter whether the destructor is virtual; it either has observable
effects or it doesn’t, and its virtuality doesn’t change the outcome.

In the second case, it does matter. When ~X is virtual, the expression may end up invoking a
derived destructor, and whether ~X itself has no effects is irrelevant.

Conflating these two meanings creates problems in two places. First, literal types are required
to have trivial destructors. This needlessly prohibits types with virtual destructors from being
literal. For example,

struct X1
{
virtual int f() const;
};

is literal, but

struct X2
{
virtual ~X2() = default;
virtual int f() const;
};

isn’t.

Second, it classifies certain destructors as non-trivial even when they satisfy the requirement
that an expression invoking them has no effects:

struct X3
{
X2 x2;
};

~X3 in this example is not virtual and has no effects, but the transitive requirement for ~X2
to be trivial renders ~X3 nontrivial as well.

Proposed Changes

(All edits are relative to N4741.)

Change 15.4 [class.dtor] p6 as follows:

A destructor is quasi- trivial if it is not user-provided and if:

— the destructor is not virtual,

— all of the direct base classes of its class have quasi- trivial destructors, and

— for all of the non-static data members of its class that are of class type (or array thereof), each such
class has a quasi- trivial destructor.

Otherwise, the destructor is not quasi- trivial.

Add a new paragraph below the above one:

A destructor is trivial if it is quasi-trivial and it is not virtual.

Change 6.7 [basic.type] p10 bullet 10.5.1 as follows:

— it has a quasi- trivial destructor,

Example

The standard library provides, in [syserr], a framework for reporting error codes. This
framework classifies error codes into categories, represented by singleton values of types
derived from the base class error_category.

error_category, and types derived from it such as generic_category, could easily be made
literal if not for the fact that ~error_category is specified as virtual. This precludes
categories to be made constexpr variables, a problem this proposal solves.

The reason for the wish to make categories constexpr is subtle. Category equality is
currently specified as identity comparison, this == &rhs. But there are platforms on
which it’s hard to guarantee that all instances of generic_category and system_category
have the same address. On such a platform it’s better for equality to be content-based rather
than address-based, for instance by storing an unique identifier in the category, so that all
generic_category instances compare equal.