开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜