我试图实现自己的小型分配器用于测试目的,在设计它时我认为我不知道如何在不违反严格别名规则的情况下实现它。

在 GitHub 的大多数开源中,C++ 分配器方法allocate<T>是内存的适配器:它运行到内存,请求N连续的std::bytes 或unsigned chars,然后reinterpret_cast将此内存分配给T,然后将内存返回给其被调用者。

但是,这如何不违反严格的别名规则(即使被调用者必须调用构造函数本身,我们也会强制转换std::byte*T*)。在实现适合大多数 STL 容器的简单缓冲区分配器时,如何解决这个问题?

7

  • 4
    实现不必遵循 C++ 规则!


    – 


  • 1
    相关/欺骗:


    – 

  • 2
    严格别名不是重新解释指针,而是取消引用它们。简单地返回指向原始内存的指针并不会破坏严格的别名。


    – 


  • 1
    C 程序员经常说不可能在符合 C 语言中编写与 malloc 等效的内容。出于同样的原因:此时您必须找到一些内存并确定 1/ 它最初没有类型,2/ 您可以祝福它成一种类型。但出于安全原因,用户级程序不允许这样做。


    – 

  • 1
    另一个相关/欺骗:它提出了基本相同的问题,即“内存分配器如何在不违反严格别名规则的情况下工作”还有


    – 



2 个回答
2

当您假装某个内存位置存在对象但实际上并不存在时,就会违反。 (您可以通过reinterpret_cast引用指针/引用然后取消引用它来实现这一点,其中只有引用本身才是 UB。)

它不会阻止您使用placement-new来更改某些内存中存储的对象类型,然后访问它。

例如:

#include <string>

int main()
{
    alignas(std::string) char buf[sizeof(std::string)];

    *reinterpret_cast<std::string*>(buf) = "foo"; // UB

    std::string *ptr = new(buf) std::string;
    *ptr = "foo"; // Legal.
}

2

  • 在我看来,提到也可能与此相关。


    – 

  • 2
    @JesperJuhl 耸耸肩。它有些相关,但您可以在没有它的情况下使用分配器。


    – 

不构造任何类型T1的对象,但是。如果您继续在指向的内存中开始 a 的生命周期,那么reinterpret_castavoid *到 a就可以了。T *T

  1. allocate确实启动 数组的生命周期T,但不启动T该数组的子对象。