显式调用模板参数类型的析构函数,即使在内置实例上实例化
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()
版本。
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 typevoid
. 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()
版本。