Явный деструктор в шаблоном контексте

Я хочу явно уничтожить vector в шаблоном контексте. Следующие работы для меня (GNU С++ 4.3, 4.4 и Clang++ 1.1):

template <typename T>
void destroy_vector_owner(VectorOwner<T> *obj)
{
    obj->v.~vector();
    // further cleanup by Python API functions omitted
}

пока он не работает в Mac OS X v10.5 g++ (i686-apple-darwin10-gcc-4.2.1) с

expected class-name before ‘(’ token

Если я изменю его на

obj->v.~vector<T>();

код не скомпилируется с g++, но Clang все еще может его обработать. Какая правильная идиома? Известно ли, что какой-либо из этих компиляторов нарушен в этом отношении?

Обновить: определение VectorOwner

template <typename T>
struct VectorOwner {
  PyObject_HEAD
  std::vector<T> v;
};

Это объект Python, который должен содержать std::vector. Я признаю, что конструкция немного опасна, но мне нужно компактное хранилище, амортизированное O (1) push_back и возможность украсть другое векторное содержимое с помощью элемента swap.

Ответ 1

Мой первый ответ был неправ, на самом деле, litb указал мне в правильном направлении. Правильный ответ что оба синтаксиса верны:


Синтаксис вызова деструктора.

Синтаксис для явного вызова деструктора описан в 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 можно найти в 7.1.5.2 Simple type specifiers:

type-name:
    class-name
    enum-name
    typedef-name

class-name описывается в 9. Classes:

class-name:
    identifier
    template-id

Таким образом, вызов деструктора упрощен, один из следующих

foo.~typedef-name ()
foo.~identifier   ()
foo.~template-id  ()

Мы не имеем здесь typedef-имя или простой идентификатор, поэтому остается только foo.~template-id() для нас.


Предположение компилятора при вызове деструктора с шаблоном-аргументами.

Мы также находим в 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.

Поэтому компилятор должен принять в вашем примере, что < - это начало списка шаблонов-аргументов.

Кроме того, если ваш деструктор будет шаблоном (...), то

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.

Итак, потому что вы не префикс вашего деструкторного вызова f.~foo<int> с помощью шаблона, т.е. например f.template ~foo<int>, компилятор должен предположить, что ваш деструктор НЕ является шаблоном.

BackTrack.

Далее

6   A template-id that names a class template specialization
    is a class-name (clause 9).

Итак, ~foo<int> называет вашу специализацию шаблона foo<int> и поэтому является class-name, a class-name - по правилам грамматики a type-name, а a ~, за которым следует a typename вызов деструктора. Поэтому

foo<int> f;
f.~foo<int>(); // valid

Деструкторный вызов без шаблонных аргументов.

Но также

f.~foo(); // valid

Потому что 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. [...]

таким образом, в f.~foo();, foo просматривается внутри f., и в рамках foo<int> он действителен ссылаться на него только с помощью foo.


Стандарт на самом деле является явным в этой теме, d'oh.

И, наконец, 14.3 содержит одноразовое разрешение:

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]

Ответ 2

От n3290, 3.4.5 Доступ к члену класса [basic.lookup.classref]

3 Если unqualified-id - это имя типа, имя типа отображается в контекст всего постфиксного выражения. Если тип T выражение объекта имеет тип класса C, также видна имя типа в рамках класса С. По крайней мере один из поисковых имя, которое относится к (возможно, с квалификацией) T. [...]

Ниже приведен пример (как ненормативная заметка), который содержит следующий фрагмент кода:

a->~A(); // OK: lookup in *a finds the injected-class-name

В частности, для template<typename T, typename Allocator> class vector;, vector - имя введенного класса. По этой причине, я считаю,

obj->v.~vector();

является правильным.

(Мне нечего сказать о ~vector<T> на данный момент.)

Ответ 3

Вы можете попробовать следующий синтаксис, который он работает в gcc:

obj->v.template ~vector<T>();

Демо.