This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++14 status.

### 2049. `is_destructible` is underspecified

Section: 20.15.5.4 [meta.unary.prop] Status: C++14 Submitter: Daniel Krügler Opened: 2011-04-18 Last modified: 2016-01-28

Priority: Not Prioritized

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with C++14 status.

Discussion:

The conditions for the type trait `is_destructible` to be true are described in Table 49 — Type property predicates:

> For a complete type `T` and given
> `template <class U> struct test { U u; };`,
> `test<T>::~test()` is not deleted.

This specification does not say what the result would be for function types or for abstract types:

1. For an abstract type `X` the instantiation `test<X>` is already ill-formed, so we cannot say anything about whether the destructor would be deleted or not.
2. In regard to function types, there exists a special rule in the core language, 13.4.2 [temp.arg.type] p. 3, which excludes member functions to be declared via the type of the template parameter:

   > If a declaration acquires a function type through a type dependent on a template-parameter and this causes a declaration that does not use the syntactic form of a function declarator to have function type, the program is ill-formed.

   > [ Example:

   > >     template<class T> struct A {
   > >       static T t;
   > >     };
   > >     typedef int function();
   > >     A<function> a; // ill-formed: would declare A<function>::t
   > >                    // as a static member function

   > — end example ]

   which has the same consequence as for abstract types, namely that the corresponding instantiation of `test` is already ill-formed and we cannot say anything about the destructor.

To solve this problem, I suggest to specify function types as trivially and nothrowing destructible, because above mentioned rule is very special for templates. For non-templates, a typedef can be used to introduce a member as member function as clarified in 9.3.4.6 [dcl.fct] p. 10.

For abstract types, two different suggestions have been brought to my attention: Either declare them as unconditionally non-destructible or check whether the expression

>     std::declval<T&>().~T()

is well-formed in an unevaluated context. The first solution is very easy to specify, but the second version has the advantage for providing more information to user-code. This information could be quite useful, if generic code is supposed to invoke the destructor of a reference to a base class indirectly via a delete expression, as suggested by Howard Hinnant:

>     template <class T>
>     my_pointer<T>::~my_pointer() noexcept(is_nothrow_destructible<T>::value)
>     {
>        delete ptr_;
>     }

Additional to the `is_destructible` traits, its derived forms `is_trivially_destructible` and `is_nothrow_destructible` are similarly affected, because their wording refers to "the indicated destructor" and probably need to be adapted as well.

[ 2011 Bloomington ]

After discussion about to to handle the exceptional cases of reference types, function types (available by defererencing a function pointer) and `void` types, Howard supplied proposed wording.

[ 2011-08-20 Daniel comments and provides alternatives wording ]

The currently proposed wording would have the consequence that every array type is not destructible, because the pseudo-destructor requires a scalar type with the effect that the expression

>     std::declval<T&>().~T()

is not well-formed for e.g. `T` equal to `int[3]`. The intuitive solution to fix this problem would be to adapt the object type case to refer to the expression

>     std::declval<U&>().~U()

with `U` equal to `remove_all_extents<T>::type`, but that would have the effect that arrays of unknown bounds would be destructible, if the element type is destructible, which was not the case before (This was intentionally covered by the special "For a complete type T" rule in the FDIS).

Suggestion: Use the following definition instead:

> Let `U` be `remove_all_extents<T>::type`.
> For incomplete types and function types, `is_destructible<T>::value` is `false`.
> For object types, if the expression `std::declval<U&>().~U()` is well-formed
> when treated as an unevaluated operand (Clause 5), then `is_destructible<T>::value`
> is `true`, otherwise it is `false`.
> For reference types, `is_destructible<T>::value` is `true`.

This wording also harmonizes with the "unevaluated operand" phrase used in other places, there does not exist the definition of an "unevaluated context"

Note: In the actually proposed wording this wording has been slightly reordered with the same effects.

Howard's (old) proposed resolution:

> Update 20.15.5.4 [meta.unary.prop], table 49:

> `template <class T> struct is_destructible;` | For a complete type `T` and given `template <class U> struct test { U u; };`, `test<T>::~test()` is not deleted. For object types, if the expression: `std::declval<T&>().~T()` is well-formed in an unevaluated context then `is_destructible<T>::value` is `true`, otherwise it is `false`. For `void` types, `is_destructible<T>::value` is `false`. For reference types, `is_destructible<T>::value` is `true`. For function types, `is_destructible<T>::value` is `false`. | `T` shall be a complete type, (possibly cv-qualified) `void`, or an array of unknown bound.

[2012, Kona]

Moved to Tentatively Ready by the post-Kona issues processing subgroup.

[2012, Portland: applied to WP]

Proposed resolution:

Update 20.15.5.4 [meta.unary.prop], table 49:

`template <class T> struct is_destructible;` | For a complete type `T` and given `template <class U> struct test { U u; };`, `test<T>::~test()` is not deleted. For reference types, `is_destructible<T>::value` is `true`. For incomplete types and function types, `is_destructible<T>::value` is `false`. For object types and given `U` equal to `remove_all_extents<T>::type`, if the expression `std::declval<U&>().~U()` is well-formed when treated as an unevaluated operand (Clause 7 [expr]), then `is_destructible<T>::value` is `true`, otherwise it is `false`. | `T` shall be a complete type, (possibly cv-qualified) `void`, or an array of unknown bound.