Mat*_* M. 8 c++ optimization language-lawyer
让我们从一些背景开始.
自定义内存池使用类似于以下内容的代码:
struct FastInitialization {};
template <typename T>
T* create() {
static FastInitialization const F = {};
void* ptr = malloc(sizeof(T));
memset(ptr, 0, sizeof(T));
new (ptr) T(F);
return reinterpret_cast<T*>(ptr);
}
Run Code Online (Sandbox Code Playgroud)
这个想法是,当调用时FastInitialization,构造函数可以假设存储已经零初始化,因此只初始化那些需要不同值的成员.
然而,GCC(至少6.2和6.3)有一个"有趣"的优化.
struct Memset {
Memset(FastInitialization) { memset(this, 0, sizeof(Memset)); }
double mDouble;
unsigned mUnsigned;
};
Memset* make_memset() {
return create<Memset>();
}
Run Code Online (Sandbox Code Playgroud)
编译为:
make_memset():
sub rsp, 8
mov edi, 16
call malloc
mov QWORD PTR [rax], 0
mov QWORD PTR [rax+8], 0
add rsp, 8
ret
Run Code Online (Sandbox Code Playgroud)
但:
struct DerivedMemset: Memset {
DerivedMemset(FastInitialization f): Memset(f) {}
double mOther;
double mYam;
};
DerivedMemset* make_derived_memset() {
return create<DerivedMemset>();
}
Run Code Online (Sandbox Code Playgroud)
编译为:
make_derived_memset():
sub rsp, 8
mov edi, 32
call malloc
mov QWORD PTR [rax], 0
mov QWORD PTR [rax+8], 0
add rsp, 8
ret
Run Code Online (Sandbox Code Playgroud)
也就是说,只有struct与其基数对应的部分的前16个字节已被初始化.调试信息确认调用memset(ptr, 0, sizeof(T));已完全省略.
另一方面,ICC和Clang都调用memset全尺寸,这是Clang的结果:
make_derived_memset(): # @make_derived_memset()
push rax
mov edi, 32
call malloc
xorps xmm0, xmm0
movups xmmword ptr [rax + 16], xmm0
movups xmmword ptr [rax], xmm0
pop rcx
ret
Run Code Online (Sandbox Code Playgroud)
所以GCC和Clang的行为有所不同,问题就变成了:GCC是否正确并且产生了更好的装配,或者是Clang是否正确和GCC车?
或者,就语言律师而言:
在哪种情况下,构造函数可以依赖存储在其分配的存储中的先前值?
注意:我认为这仅适用于展示位置new,但我很高兴以其他方式展示.
Bar*_*rry 11
可能放置
new依赖于底层存储价值?
不,它可能不会.来自[dcl.init]:
如果没有为对象指定初始化程序,则默认初始化该对象.当获得具有自动或动态存储持续时间的对象的存储时,该对象具有不确定的值,并且如果没有对该对象执行初始化,则该对象保留不确定的值,直到该值被替换(5.18).
不确定的价值就是那个,不确定的.这并不意味着,在放置新表达式的情况下,必须保持先前的存储器.允许编译器对内存执行任何操作 - 包括但不限于任何内容.
在哪种情况下,构造函数可以依赖存储在其分配的存储中的先前值?
在[dcl.init]列表案件的后续款,其中的行为是不产生不确定的值时,但他们只有无符号窄字符类型做不确定的.
所以,在任何情况下.