I want to explicitly destroy a vector
in a templated context. The following works for me (GNU C++ 4.3, 4.4 and Clang++ 1.1):
template <typename T>
void destroy_vector_owner(VectorOwner<T> *obj)
{
obj->v.~vector();
// further cleanup by Python API functions omitted
}
while it fails on Mac OS X v10.5's g++
(i686-apple-darwin10-gcc-4.2.1
) with
expected class-name before ‘(’ token
If I change it to
obj->v.~vector<T>();
the code fails to compile with G++, but Clang can still handle it. Which is the correct idiom? Are any of these compilers known to be broken in this regard?
Update: the definition of VectorOwner
is
template <typename T>
struct VectorOwner {
PyObject_HEAD
std::vector<T> v;
};
This is a Python object that has to keep an std::vector
alive. I admit that the construct is slightly dangerous, but I need the compact storage, amortized O(1) push_back
and the ability to steal another vector's contents with the swap
member.
An explicit call to destructor is only necessary when an object is placed at a particular location in memory by using placement new. Destructor should not be called explicitly when the object is dynamically allocated because the delete operator automatically calls destructor.
When delete is used to deallocate memory for a C++ class object, the object's destructor is called before the object's memory is deallocated (if the object has a destructor). If the operand to the delete operator is a modifiable l-value, its value is undefined after the object is deleted.
You can call the destructor as: object. ~TYPE();
A trivial destructor is a destructor that performs no action. Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage. All data types compatible with the C language (POD types) are trivially destructible.
My first answer was wrong actually, litb pointed my into the right direction. The right answer is that both syntaxes are correct:
The syntax for an explicit destructor call is described in 12.4 Destructors
:
12 In an explicit destructor call, the destructor name appears
as a ˜ followed by a type-name that names the destructor’s
class type. The invocation of a destructor is subject to the
usual rules for member functions (9.3) [...]
type-name
can be found in 7.1.5.2 Simple type specifiers
:
type-name:
class-name
enum-name
typedef-name
class-name
is described in 9. Classes
:
class-name:
identifier
template-id
So a destructor call is, simplified, one of the following
foo.~typedef-name ()
foo.~identifier ()
foo.~template-id ()
We neither have a typedef-name here, nor a simple identifier, so only foo.~template-id()
is left
for us.
We also find in 14. Templates
3 After name lookup (3.4) finds that a name is a template-name,
if this name is followed by a <, the < is always taken as the
beginning of a template-argument-list and never as a name
followed by the less-than operator.
So the compiler must assume in your example that the <
is the beginning
of a template-argument-list.
Also, if your destructor would be a template (...), then
4 When the name of a member template specialization appears
after . or -> in a postfix-expression, or after nested-name-specifier
in a qualified-id, and the postfix-expression or qualified-id explicitly
depends on a template-parameter (14.6.2), the member template name must
be prefixed by the keyword template. Otherwise the name is assumed to
name a non-template.
So because you did not prefix your destructor call f.~foo<int>
with template, i.e.
like f.template ~foo<int>
, the compiler must assume that your destructor
is NOT a template.
Further,
6 A template-id that names a class template specialization
is a class-name (clause 9).
So ~foo<int>
names your template specialization foo<int>
and therefore is a class-name
,
a class-name
is by the grammar rules a type-name
, and a ~
followed by a typename
is
a destructor call. Therefore
foo<int> f;
f.~foo<int>(); // valid
But also
f.~foo(); // valid
Because 3.4.5 Class member access
:
3 If the unqualified-id is ˜type-name, and the type of the object expression
is of a class type C (or of pointer to a class type C), the type-name is
looked up in the context of the entire postfix-expression and in the scope of
class C. [...]
thus in f.~foo();
, foo
is looked up within f.
, and within the scope of foo<int>
, it is valid
to refer to it just with with foo
.
And finally, 14.3 contains the one-and-for-all-permission:
5 An explicit destructor call (12.4) for an object that
has a type that is a class template specialization may
explicitly specify the template-arguments. [Example:
template<class T> struct A {
˜A();
};
void f(A<int>* p, A<int>* q) {
p->A<int>::˜A(); // OK: destructor call
q->A<int>::˜A<int>(); // OK: destructor call
}
—end example]
From n3290, 3.4.5 Class member access [basic.lookup.classref]
3 If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression. If the type T of the object expression is of a class type C, the type-name is also looked up in the scope of class C. At least one of the lookups shall find a name that refers to (possibly cv-qualified) T. [...]
Following that is an example (as a non-normative note) which contains the following snippet of code:
a->~A(); // OK: lookup in *a finds the injected-class-name
In particular, for template<typename T, typename Allocator> class vector;
, vector
is the injected-class-name. For that reason, I believe
obj->v.~vector();
is correct.
(I don't have anything to say about ~vector<T>
at the moment.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With