显式调用模板参数类型的析构函数,即使在内置实例化时也是如此

时间:2015-05-13 06:05:23

标签: c++ language-lawyer

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 ++思想)允许模板参数类型的“显式析构函数调用”,即使手动执行类型替换,也会导致语法错误(从{{1}开始)实际上并没有真正的析构函数来调用。)

更明确地说,这一行:

bool

((T*)buffer)->~T(); 上设置T时编译并运行正常,但对实际的bool执行相同的操作:

bool

是语法错误。

我发现这种行为,因为我正在进行模板元编程并发现它非常有用,所以我猜它是标准的,并且专门用于处理像我上面那样的情况。有没有人确切知道这是否真的如此,并且当这种行为被标准化时,如果是这样的话?

此外,任何人都可以指出标准中的确切措辞是什么,以及它允许​​的情况范围? (我不擅长阅读标准,所以我很难自己解决这个问题。)

1 个答案:

答案 0 :(得分:11)

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

  

1在点.或箭头->后使用伪析构函数名称   operator表示由非表示类型的析构函数    type-name decltype-specifier 。结果只能用作函数调用操作符()的操作数,以及结果   这样的调用具有类型void。唯一的影响是评估   点或箭头前的 postfix-expression

     

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

type-name 是(§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),它是类型名称,因此{{ 1}}版本编译。