Explicit destructor in templated context
I want to explicitly destroy a vector
i开发者_如何学Cn 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.
My first answer was wrong actually, litb pointed me into the right direction. The right answer is that both syntaxes are correct:
Destructor call syntax.
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.
Compiler's assumption on destructor call with template-arguments.
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.
Backtrack.
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
Destructor call without template-arguments.
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
.
The standard is actually explicit on this topic, d'oh.
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.)
You can try following syntax it works in gcc as well:
obj->v.template ~vector<T>();
Demo.
精彩评论