Explicit destructor in templated context

梦想的初衷 提交于 2019-12-01 03:17:25

My first answer was wrong actually, litb pointed my 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!