22

我正在阅读的 STL 代码可能很旧......但问题与 C++ 模板语法更相关。

问题围绕着这个 stl 模板函数:

template<class T> std::destroy(T *p) {
    p->~T();
}

我似乎找不到 std::destroy(T *) 函数的特化。所以在我看来,模板函数将为“int”类型实例化相同,并调用“int”的析构函数。为了说明我的观点,我创建了这个模拟 std::destroy 的示例代码。我称它为 my_destroy 在这个例子中。

#include <iostream>
#include <stdio.h>
using namespace std;

template <class T> 
void my_destroy(T * pointer) {
    pointer->~T(); 
}
int main()
{
    int *a;
    //a->~int();        // !!! This won't compile.
    my_destroy<int>(a); // !!! This compiles and runs.
}

}

令我惊讶的是,这一行无法编译:

a->~int();

但是这一行编译:

my_destroy<int>(a);

我的困惑是,我认为这my_destroy<int>(a)将被实例化为a->~int();

对于更大范围内的一个问题,当一个 STL 容器<int>擦除一个元素时,它是如何std::destroy()工作的?

4

3 回答 3

33

请注意,虽然a->~int();不编译,但这样做:

typedef int INT;
int* a;
a->~INT();

从标准:

5.2.4p1pseudo-destructor-name点后的使用。type-nameor 箭头 -> 运算符表示由or表示的非类类型的析构函数decltype-specifier。结果只能用作函数调用运算符 () 的操作数,并且这种调用的结果类型为 void。唯一的影响是在点或箭头之前评估后缀表达式。

从 5.2p1 开始:

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

最后,7.1.6.2p1:

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

所以,奇怪的int是,在语法上不是 a type-name(它是 a simple-type-specifier),所以你不能调用~int(),但是INTis ,所以你可以。

于 2013-07-19T21:45:27.367 回答
4

对于类类型(非标量类型),表达式

pointer->~T();

本质上是一个函数调用(一个后缀表达式)。要识别要调用的函数,pointer->~T必须分析零件。这~T是一个id 表达式IFFT是一个类名,标识析构函数。

当然,int不是类名。但是如果T是一个命名标量类型的类型名称,则相同的表达式会以不同的方式解析。整个部分pointer->~T被标识为一个特殊的后缀表达式,称为伪析构函数名称。下面()的表达式被认为是对伪析构函数的调用([expr.pseudo] 中的规则禁止使用伪析构函数名称做任何其他事情,但要调用它)。

int本身不是类型名称[dcl.type.simple],而是简单类型说明符

类型名称:

  • 班级名称
  • 枚举名称
  • 类型定义名称
  • 简单模板 ID

这就是为什么您可以使用typedef'dintT在您的示例 (*) 中,但不能int直接使用。sehe已经很好地解释了这个推理。

伪析构函数调用的规则在 [expr.pseudo] 中指定:“唯一的效果是在点或箭头之前计算后缀表达式。”

(*) 来自 [temp.param]/3:“标识符不跟随省略号的类型参数将其标识符定义为typedef-name [...]”

于 2013-07-19T21:46:43.507 回答
3

该语言允许这些东西启用通用编程。但是,您的“直接”调用不在通用代码中,因此它失败了。

另一个这样的案例是

template <typename T> void foo()
{
    T instance = T(); // default constructor
}

foo<int>(); // will work

所以,是的:

template <typename T> void foo()
{
    T* instance = new T(); // default constructor
    delete instance;      
}

也适用于内置原始类型,因为T它是模板范围内的类型名。

于 2013-07-19T21:31:03.587 回答