Явный вызов деструктора типа параметра шаблона, даже при создании экземпляра встроенного - программирование

Явный вызов деструктора типа параметра шаблона, даже при создании экземпляра встроенного

Программа С++ (несколько неожиданно, во-первых, для меня) компилируется и работает нормально, за исключением строки, прокомментированной в конце main(), которая является ошибкой во время компиляции, если она раскоментирована.

#include <typeinfo>
#include <iostream>

struct Foo {
    int x;
};

template <typename T>
void create(char *buffer)
{
    std::cout << "creating " << typeid(T).name() << std::endl;
    new (buffer) T();
}

template <typename T>
void destroy(char *buffer)
{
    std::cout << "destroying " << typeid(T).name() << std::endl;
    ((T*)buffer)->~T();
}

int main(int argc, char **argv)
{
    char buffer[sizeof(Foo) > sizeof(bool) ? sizeof(Foo) : sizeof(bool)];

    // create/destroy Foo via template function calls
    create<Foo>(buffer);
    destroy<Foo>(buffer);
    // now do the above explicitly...
    new (buffer) Foo();
    ((Foo*)buffer)->~Foo();

    // create/destroy bool via template function calls
    create<bool>(buffer);
    destroy<bool>(buffer);
    // now do the above explicitly...
    new (buffer) bool();
    // ((bool*)buffer)->~bool(); // oops, doesn't work

    return 0;
}

Я понимаю из этого, что С++ (или, по крайней мере, g++ идея С++) допускает "явный вызов деструктора" типа параметра шаблона, даже если вручную выполнение замены типа приводит к синтаксической ошибке (поскольку bool doesn На самом деле на самом деле есть деструктор для вызова).

Чтобы быть более явным, строка:

((T*)buffer)->~T();

компилируется и работает нормально, когда T устанавливается на bool, но делает то же самое с фактическим bool:

((bool*)buffer)->~bool();

является синтаксической ошибкой.

Я обнаружил это поведение, поскольку я делал метапрограммирование шаблона и считаю его очень полезным, поэтому я предполагаю, что он стандартный и был добавлен специально для обработки таких случаев, как тот, который у меня выше. Кто-нибудь знает наверняка, действительно ли это так, и когда это поведение стандартизировано, если оно есть?

Кроме того, может ли кто-нибудь указать, что такое точная формулировка в стандарте, что позволяет это, и объем ситуаций, которые он разрешает? (Я не умею читать стандартизированный, поэтому мне сложно понять это.)

4b9b3361

Ответ 1

Это действительно действительный С++ (и был с тех пор как С++ 98, насколько я знаю) и известен как вызов псевдо-деструктора. N4431 §5.2.4 [expr.pseudo]:

1 Использование псевдодеструктора-имени после точки . или стрелки ->оператор представляет деструктор для типа некласса, обозначаемого тип-имя или спецификатор decltype. Результат должен использоваться только как операнд для оператора функции (), а результат такой вызов имеет тип void. Единственный эффект - это оценка postfix-выражение перед точкой или стрелкой.

2 Левая часть точечного оператора должна быть скалярного типа. левая сторона оператора стрелки должна иметь указатель на скалярный тип. Этот скалярный тип является типом объекта. Cv-безусловные версии типа объекта и типа, обозначенного псевдо-деструктор-имя должно быть одного типа. Кроме того, два имени типа в псевдо-деструкторе-имени формы

nested-name-specifier_opt type-name :: ~ type-name

должен обозначать один и тот же скалярный тип.

Псевдо-деструктор-имя является одним из (§5.2 [expr.post]):

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

Хотя имя типа является одним из (§7.1.6.2 [dcl.type.simple])

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

bool не является именем типа, поэтому версия ~bool() является синтаксической ошибкой. Внутри шаблона параметр типа шаблона является typedef-name (§14.1 [temp.param]/p3), который является именем типа, поэтому компилируется версия ~T().