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
是语法错误。
我发现这种行为,因为我正在进行模板元编程并发现它非常有用,所以我猜它是标准的,并且专门用于处理像我上面那样的情况。有没有人确切知道这是否真的如此,并且当这种行为被标准化时,如果是这样的话?
此外,任何人都可以指出标准中的确切措辞是什么,以及它允许的情况范围? (我不擅长阅读标准,所以我很难自己解决这个问题。)
答案 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}}版本编译。