显式调用模板参数类型的析构函数,即使在内置实例上实例化

Explicit call to destructor of template parameter type, even when instantiated on a builtin

C++ 程序(一开始对我来说有点出乎意料)编译并运行良好,除了在 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;
}

我从中了解到 C++(或者至少是 g++ 的 C++ 思想)允许模板参数类型的 "explicit destructor call",即使自己手动进行类型替换会导致语法错误(因为 bool 实际上并没有调用析构函数)。

更明确地说,行:

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

bool 上实例化 T 时编译和运行良好,但对实际 bool 做同样的事情:

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

是语法错误。

我在进行模板元编程时发现了这种行为并发现它非常有用,所以我猜它是标准的并且是专门添加来处理像我上面遇到的情况的。有谁知道是否真的是这种情况,如果是这种行为是什么时候标准化的?

此外,谁能指出标准中允许这样做的确切措辞,以及它允许​​的情况范围? (我不擅长阅读标准语,所以我很难自己弄明白。)

这确实是有效的 C++(据我所知从 C++98 开始就是这样),并且被称为 伪析构函数调用 。 N4431 §5.2.4 [expr.pseudo]:

1 The use of a pseudo-destructor-name after a dot . or arrow -> operator represents the destructor for the non-class type denoted by type-name or decltype-specifier. The result shall only be used as the operand for the function call operator (), and the result of such a call has type void. The only effect is the evaluation of the postfix-expression before the dot or arrow.

2 The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type is the object type. The cv-unqualified versions of the object type and of the type designated by the pseudo-destructor-name shall be the same type. Furthermore, the two type-names in a pseudo-destructor-name of the form

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

shall designate the same scalar type.

A 伪析构函数名称 是 (§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

虽然 type-name 是 (§7.1.6.2 [dcl.type.simple])

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

bool 不是 type-name,因此 ~bool() 版本是语法错误。在模板内部,模板类型参数是一个 typedef-name (§14.1 [temp.param]/p3),它是一个 type-name,所以编译 ~T() 版本。