С++ 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()
?
Ответы
Ответ 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
является именем в области шаблона.