在基本类型的向量/数组中放置新的派生类型是否合法

Ng *_*ang 7 c++ polymorphism virtual undefined-behavior language-lawyer

在定义为基础对象的向量/数组中放置新的派生对象是否合法

#include <iostream>
#include <memory>
#include <vector>

struct A
{
    virtual ~A() 
    {
        std::cout << "~A()" << std::endl;
    }
};

struct B : public  A
{
    virtual ~B()
    {
        std::cout << "~B()" << std::endl;
    }
};

int main()
{
    std::vector<A> a(1);
    a[0].~A();
    :: new(std::addressof(a[0])) B();
}
Run Code Online (Sandbox Code Playgroud)

这是合法的 C++ 吗?

这个问题与这里的问题有关。讨论的要点是我们能够在编译时确定向量/数组中对象的类型,因此我们不必进行任何动态分派。我们可以使用这些信息来优化 gcc 现在正在做的事情。

我在这里向 gcc 提出了一个问题,反对这种优化的论点是,我们可能会放置新的内容来更改存储在向量/数组中的类型,但我们不确定它是否合法,因此出现了这个 stackoverflow 问题。

有趣的是,对于数组,gcc 的行为与 clang 不同,并且 gcc 本身根据数组的长度有不同的行为。当 len >= 3 时,它执行虚拟 dtor,但当 len < 3 时,它执行合格的 dtor 调用。

https://godbolt.org/z/f33Gh5EGM

编辑:

问题纯粹是“这是合法的 C++ 吗”。问题中的任何其他信息都是为​​了说明我为什么要问这样的问题。

Mes*_*kon -1

注意:这个结论是基于语义的,::delete[]并且可能会或可能不会转移到自动存储阵列的破坏。

如果我们查看::delete[]then的文档,我们可以找到关于删除数组类型的声明

对于第二种(数组)形式,表达式必须是空指针值或先前由 new-expression 的数组形式获得的指针值,其分配函数不是非分配形式(即重载(10))。表达式指向的类型必须与数组对象的元素类型相似。如果表达式是其他任何内容,包括如果它是通过 new-expression 的非数组形式获得的指针,则行为未定义。

那么表达式的指向类型必须与数组的元素类型相似是什么意思呢?

如果我们看看如何similar定义,那么我们会发现这个语句

非正式地,如果忽略顶级简历资格,两种类型是相似的:
它们是相同的类型;或者
都是指针,且指向的类型相似;或者
都是指向同一个类的成员的指针,并且所指向的成员的类型相似;或者
它们都是相同大小的数组或都是未知边界的数组,并且数组元素类型相似。(C++20 之前)
它们都是相同大小的数组,或者至少其中一个是未知边界数组,并且数组元素类型相似。(自 C++20 起)

假设数组的元素类型是 ,A指向的类型是 ,我们可以通过that和are notB的非正式定义看到。因此,根据这种行为是未定义的。similarABsimilar::delete[]

  • 这个答案的基础是错误的;`vector` 使用 `allocator_traits` 来分配/释放存储,但不使用显式对象指针;从未使用过“delete[]”,因此这些都无关紧要。`vector` 中使用的 [`allocator::deallocate`](https://en.cppreference.com/w/cpp/memory/allocator/deallocate) 的定义从不调用 `delete[]` --所以这些都不适用。销毁也是使用 [`allocator_traits::destroy`](https://en.cppreference.com/w/cpp/memory/allocator_traits/destroy) 按元素完成的 (3认同)