is_destructible
is underspecifiedSection: 21.3.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:
X
the instantiation test<X>
is already ill-formed, so we cannot say anything about whether the destructor
would be deleted or not.which has the same consequence as for abstract types, namely that the corresponding instantiation ofIf 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 ]
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).
Let
U
beremove_all_extents<T>::type
.
For incomplete types and function types,is_destructible<T>::value
isfalse
.
For object types, if the expressionstd::declval<U&>().~U()
is well-formed
when treated as an unevaluated operand (Clause 5), thenis_destructible<T>::value
istrue
, otherwise it isfalse
.
For reference types,is_destructible<T>::value
istrue
.
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 21.3.5.4 [meta.unary.prop], table 49:
template <class T> struct is_destructible;
For a complete typeT
and giventemplate <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 thenis_destructible<T>::value
istrue
, otherwise it isfalse
.
Forvoid
types,is_destructible<T>::value
isfalse
.
For reference types,is_destructible<T>::value
istrue
.
For function types,is_destructible<T>::value
isfalse
.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 21.3.5.4 [meta.unary.prop], table 49:
template <class T>
struct is_destructible; |
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 anunevaluated 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. |