在潜入动态记忆的过程中,我发现看起来矛盾的是琐碎的类型如何开始它们的生命.考虑一下片段
void* p = ::operator new(sizeof(int)); // 1
// 2
new (p) int; // 3
Run Code Online (Sandbox Code Playgroud)
什么时候int开始它的生命?
仅获取存储,::operator new指定具有效果(来自[new.delete.single])
new-expression调用的分配函数用于分配大小的存储字节.[...]分配适当对齐的存储空间以表示该大小的任何对象,前提是对象的类型没有新扩展的对齐方式.
鉴于获取存储不足以创建对象,int因此无法在此处开始其生命周期.
此时,int已经获得了适合的存储空间.
这int是由新位置创建的.但不知何故,它的生命没有从这里开始,因为来自[basic.life]
[...]如果一个对象属于类或聚合类型,并且它或其子对象之一由除了普通默认构造函数之外的构造函数初始化,则称该对象具有非空的初始化.类型对象的生命周期从以下时间
T开始:
获得具有适当对齐和类型大小的存储
T,并且如果对象具有非空的初始化,则其初始化完成[...]
int既不是类也不是聚合类型,因此它具有空的初始化.因此,只有第一颗子弹适用.然而,这显然不是在获得存储时,因此不能在其寿命开始时.
分配器需要在不构造其元素的情况下返回内存.然而,这对于琐碎的类型来说没有意义.的影响a.allocate(n)与a对类型的分配器对象T是
n为类型的T对象分配内存,但不构造对象.
在 C++20 中接受P0593R6(“为低级对象操作隐式创建对象”)后,C++23 将得到std::start_lifetime_as()“完成 [P0593R6] 中提出的功能”(参见P2590R2、P2679R2和cppreference C++ 23 功能测试页)。
参考实现是什么样子std::start_lifetime_as()的?
这样的事情就足够了,还是还有更多?
#include <cstddef>
#include <new>
template<class T>
T* start_lifetime_as(void* p) noexcept
{
new (p) std::byte[sizeof(T)];
return static_cast<T*>(p);
}
Run Code Online (Sandbox Code Playgroud) std :: aligned_storage :: type是POD类型.POD类型可以memcpy.但是,如果将新的非平凡可复制类型放置到std :: aligned_storage会发生什么?它能记得那个std :: aligned_storage吗?
非平凡可复制类型(非POD类型)不能memcpy,行为未定义.如果std :: aligned_storage memcpy是非平凡可复制的类型,它是否也是未定义的行为?
#include <new>
#include <type_traits>
#include <cstring>
#include <iostream>
struct y { int a; } ;
// non-trivially-copyable
struct t
{
y a;
int* p;
t(){ p = new int{ 300 }; }
t( t const& ){ a.a += 100; }
~t(){ delete p; }
};
int main()
{ // Block 1
{
t a; a.a.a = 100;
t b; b.a.a = 200;
// std::memcpy(&b,&a,sizeof(t)); // abort...non-trivially-copyable
} …Run Code Online (Sandbox Code Playgroud) 据我所知,memcpy进入未初始化的存储不能安全地用于创建源对象的副本。
但是,在去年 open-std WG21“ub”列表上的这个线程中,参与者提到了新的 memcpy 生命周期启动规则:
这似乎没有上升到错误报告的级别,但它可能与新的 memcpy 生命周期启动规则有关。他们会为源字节和目标字节分配一些静态类型吗?
Based on the context of the question and small amount of type-erasure example code, it appears that it may be related to creating new objects in aligned_storage via memcpy.
Search as I might, I can't find a reference to the new rules. I'm particularly curious if they only apply to replacing the contents of an already created object, or if they change …
我的问题是这样的:
假设类型T是可复制的。...可以在不调用构造函数的情况下“创建”该类型的实例。
#include <type_traits>
#include <cstring>
using T = int; // T can be any trivially copyable type
T create(const T& other)
{
std::aligned_storage_t<sizeof(T),alignof(T)> my_T;
std::memcpy(&my_T,&other,sizeof(T));
return *reinterpret_cast<T*>(&my_T);
}
Run Code Online (Sandbox Code Playgroud)
这是已定义的行为,还是只能复制到T类型的现有对象中?
将float值的位模式复制到a uint32_t或反之亦然(不是将它们转换),我们可以使用std::copy或者逐字节复制位memcpy.另一种方法是使用reinterpret_cast如下:
float f = 0.5f;
uint32_t i = *reinterpret_cast<uint32_t*>(&f);
Run Code Online (Sandbox Code Playgroud)
要么
uint32_t i;
reinterpret_cast<float&>(i) = 10;
Run Code Online (Sandbox Code Playgroud)
然而,有一个声称说reinterpret_cast上面使用的两个,调用未定义的行为.
真的吗?怎么样?
试图回答另一个问题,我提出了一个解决方案,用于std::memcpy()将通用类型存储在chars 的缓冲区中.
我怀疑存储POD可能存在内存对齐问题(我知道有非POD类型,因为std::string非常非常危险).
简而言之:以下程序存在内存对齐问题?
如果它们是,那么可以写一些类似的东西(将POD值存储在char缓冲区中)是安全的吗?如何?
#include <cstring>
#include <iostream>
int main()
{
char buffer[100];
double d1 { 1.2 };
std::memmove( buffer + 1, & d1, sizeof(double) );
double d2;
std::memmove( & d2, buffer + 1, sizeof(double) );
std::cout << d2 << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)