xor*_*256 9 c++ arrays placement-new language-lawyer alignas
在C中,可以使用malloc(sizeof(T)*N)分配动态数组,然后使用指针算法在此动态数组中获取i偏移的元素.
在C++中,可以使用operator new()以与malloc()相同的方式执行类似的操作,然后使用new(例如,可以在书中看到第13项的解决方案"Exceptional C++:47工程难题,编程问题和解决方案"通过Herb Sutter).如果您没有,则此问题的解决方案摘要如下:
T* storage = operator new(sizeof(T)*size);
// insert element
T* p = storage + i;
new (p) T(element);
// get element
T* element = storage[i];
Run Code Online (Sandbox Code Playgroud)
对我来说,这看起来是合法的,因为我要求一大块内存,有足够的内存来容纳n个对齐的size = sizeof(T)元素.因为sizeof(T)应该返回一个对齐的元素的大小,并且它们在一块内存中一个接一个地放置,所以在这里使用指针算法是可以的.
然而,我随后指出了以下链接:http://eel.is/c++draft/expr.add#4或http://eel.is/c++draft/intro.object#def:object并声称在C++运算符new()中没有返回一个数组对象,因此对它返回的内容进行指针算术并将其用作数组是未定义的行为,而不是ANSI C.
我不是很擅长这么低级别的东西而且我真的想通过阅读这个来理解:https ://www.ibm.com/developerworks/library/pa-dalign/或者这个:http:// jrruethe. github.io/blog/2015/08/23/placement-new/但我还是不明白,如果萨特是完全错误的?
我确实理解alignas在构造中是有意义的,例如:
alignas(double) char array[sizeof(double)];
Run Code Online (Sandbox Code Playgroud)
(c)http://georgeflanagin.com/alignas.php
如果数组看起来不在double的边界内(可能是在2字节读取处理器的结构中跟随char).
但这是不同的 - 我已经从堆/空闲存储请求内存,特别是请求的操作符new返回内存,该内存将保持与sizeof(T)对齐的元素.
总结一下这是TL; DR:
对不起,如果这是愚蠢的.
分配内存上的指针算法问题,如示例所示:
T* storage = operator new(sizeof(T)*size);
// ...
T* p = storage + i; // precondition: 0 <= i < size
new (p) T(element);
Run Code Online (Sandbox Code Playgroud)
技术上未定义的行为早已为人所知.这意味着std::vector
无法使用明确定义的行为纯粹作为库实现,但需要额外的保证,除了标准中的实现.
标准委员会的意图绝对不是无法std::vector
实现的.当然,Sutter是正确的,这样的代码旨在明确定义.标准的措辞需要反映出来.
P0593是一项提案,如果被接受进入标准,可能能够解决这个问题.与此同时,继续编写如上所述的代码是可以的; 没有主流编译器会将其视为UB.
编辑:正如在评论中指出,我应该说明的是,当我说 storage + i
下,P0593将被明确定义的,我是假设这些元素storage[0]
,storage[1]
......,storage[i-1]
在已建成.虽然我不知道我的理解P0593不够好断定它不会也涵盖这些元素的情况下,还没有已建成.
C++ 标准包含一个未解决的问题,即对象的底层表示不是“数组”,而是unsigned char
对象的“序列”。尽管如此,每个人都将其视为数组(这是有意的),因此可以安全地编写如下代码:
char* storage = static_cast<char*>(operator new(sizeof(T)*size));
// ...
char* p = storage + sizeof(T)*i; // precondition: 0 <= i < size
new (p) T(element);
Run Code Online (Sandbox Code Playgroud)
只要void* operator new(size_t)
返回正确对齐的值即可。使用sizeof
-multiplied 偏移量来保持对齐是安全的。
在 C++17 中,有一个宏STDCPP_DEFAULT_NEW_ALIGNMENT,它指定 "normal" 的最大安全对齐void* operator new(size_t)
,void* operator new(std::size_t size, std::align_val_t alignment)
如果需要更大的对齐,则应使用该宏。
在早期版本的 C++ 中,没有这样的区别,这意味着void* operator new(size_t)
需要以与任何对象的对齐方式兼容的方式来实现。
至于能够直接在 上进行指针算术T*
,我不确定标准是否需要这样做。然而,很难以一种无法工作的方式来实现 C++ 内存模型。