【问题标题】:C++ std::destroy(T * pointer)C++ std::destroy(T * 指针)
【发布时间】:2013-07-19 21:23:11
【问题描述】:

我正在阅读的 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&lt;int&gt;(a) 将被实例化为 a-&gt;~int(); 的等效项

对于一个更大的问题,当&lt;int&gt; 的STL 容器删除一个元素时,std::destroy() 是如何工作的?

【问题讨论】:

  • 我不知道为什么这被否决了,这是一个合理的问题。 +1
  • 它被称为伪析构函数,并且有自己的特殊规则。本质上,您只能使用 type-name,它可以是类/枚举名称或 typedef 名称。
  • 事实上你不能使用关键字来表示调用析构函数。 typedef int INT; a-&gt;~INT(); 编译。
  • 它被称为std::destroy_at

标签: c++ stl


【解决方案1】:

请注意,虽然 a-&gt;~int(); 无法编译,但可以:

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

来自标准:

5.2.4p1 在点之后使用pseudo-destructor-name。或箭头 -> 运算符表示由type-namedecltype-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 在语法上不是type-name(它是simple-type-specifier),所以你不能调用~int(),但INT 是,所以你可以。

【讨论】:

    【解决方案2】:

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

    pointer->~T();
    

    本质上是一个函数调用(一个后缀表达式)。要识别要调用的函数,必须分析pointer-&gt;~T 部分。 ~T 是一个id-expression IFF T 是一个类名,标识析构函数。

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

    int 本身不是一个type-name [dcl.type.simple],而是一个simple-type-specifier

    类型名称:

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

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

    伪析构函数调用的规则在 [expr.pseudo] 中指定:“唯一的效果是在点或箭头之前对 postfix-expression 求值。”

    (*) from [temp.param]/3: "一个 type-parameter 其标识符不跟在省略号后面,将其 identifier 定义为一个 类型定义名称 [...]"

    【讨论】:

    • +1 以获得出色的背景资料。我只是按照我看到的来命名它。也许我有点过于务实,无法回忆起标准语,但这肯定是有益的
    【解决方案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 是模板范围内的类型名。

    【讨论】:

    • @MooingDuck 嗯。这是因为 int instance;int instance = int(); 之间存在差异 - 后者值初始化它,而前者让它未初始化。
    猜你喜欢
    • 2015-03-01
    • 1970-01-01
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-24
    • 2012-09-07
    • 1970-01-01
    相关资源
    最近更新 更多