Подтвердить что ты не робот

С++ std:: destroy (указатель T *)

Код STL, который я читаю, может быть старым... но вопрос больше связан с грамматикой шаблонов С++.

Вопрос окружает эту шаблонную функцию stl:

template<class T> std::destroy(T *p) {
    p->~T();
}

Я не могу найти специализацию функции std:: destroy (T *). Поэтому мне кажется, что функция шаблона будет создавать экземпляры для типов "int" и вызывать "int" destructor. Чтобы выразить свою точку зрения, я создал этот образец кода, который эмулирует std:: destroy. Я называю это my_destroy в этом примере.

#include <iostream>
#include <stdio.h>
using namespace std;

template <class T> 
void my_destroy(T * pointer) {
    pointer->~T(); 
}
int main()
{
    int *a;
    //a->~int();        // !!! This won't compile.
    my_destroy<int>(a); // !!! This compiles and runs.
}

}

К моему удивлению, эта строка не компилируется:

a->~int();

Но эта строка компилируется:

my_destroy<int>(a);

Моя путаница в том, что я думал, что my_destroy<int>(a) будет создан как эквивалент a->~int();

В более крупном контексте, когда контейнер STL <int> стирает элемент, как работает std::destroy()?

4b9b3361

Ответ 1

Обратите внимание, что, хотя a->~int(); не компилируется, это делает:

typedef int INT;
int* a;
a->~INT();

Из стандарта:

5.2.4p1 Использование a pseudo-destructor-name после точки. или стрелка → представляет деструктор для неклассического типа, обозначенного символом type-name или decltype-specifier. Результат должен использоваться только как операнд для оператора вызова функции(), и результат такого вызова имеет тип void. Единственный эффект - это оценка постфиксного выражения перед точкой или стрелкой.

Из 5.2p1:

pseudo-destructor-name:
  nested-name-specifier_opt type-name :: ~ type-name
  nested-name-specifier template simple-template-id :: ~ type-name
  nested-name-specifier_opt~ type-name
  ~ decltype-specifier

И, наконец, 7.1.6.2p1:

type-name:
  class-name
  enum-name
  typedef-name
  simple-template-id

Итак, любопытно, что int не является синтаксически a type-name (это a simple-type-specifier), и поэтому вы не можете вызывать ~int(), но int есть и поэтому вы можете.

Ответ 2

Для типов классов (нескалярных типов) выражение

pointer->~T();

- это, по сути, вызов функции (постфиксное выражение). Чтобы идентифицировать функцию, подлежащую вызову, необходимо проанализировать часть pointer->~T. ~T является id-выражением IFF T - это имя класса, идентифицирующее функцию деструктора.

Конечно, int не является именем класса. Но если T - это имя типа, называющее скалярный тип, то одно и то же выражение анализируется по-разному. Вся часть pointer->~T идентифицируется как специальное постфиксное выражение, называемое псевдо-деструкторным именем. При следующем () выражение считается вызовом псевдо-деструктора (правила в [expr.pseudo] запрещают делать что-либо еще с именем псевдо-деструктора, но называть его).

int сам по себе не является именем типа [dcl.type.simple], а спецификатором простого типа:

имя-типа:

  • Класс имя
  • Перечисление имя
  • ЬурейеГо имя
  • простой шаблон-идентификатор

Вот почему вы можете использовать typedef 'd int или T, как в вашем примере (*), но не int напрямую. Обоснование было хорошо объяснено sehe.

Правила для вызовов псевдо-деструктора указаны в [expr.pseudo]: "Единственный эффект - это оценка постфиксного выражения перед точкой или стрелкой".

(*) из [temp.param]/3: "Тип-параметр, идентификатор которого не следует за многоточием, определяет его идентификатор как typedef-name [...]"

Ответ 3

Язык позволяет этим вещам разрешать общее программирование. Однако ваш "прямой" вызов не входит в общий код, поэтому он терпит неудачу.

Другим таким случаем является

template <typename T> void foo()
{
    T instance = T(); // default constructor
}

foo<int>(); // will work

Итак, да:

template <typename T> void foo()
{
    T* instance = new T(); // default constructor
    delete instance;      
}

также будет работать для встроенных примитивных типов, потому что T является именем в области шаблона.