为什么std::optional有类似指针的开销
Why does std::optional has pointer-like overhead?
问题
我在编译器资源管理器上对std::optional
进行了一些测试,令我惊讶的是,它的行为似乎像一个指针,尽管标准(§23.6.3)中规定它应该包含它:
不允许实现使用额外的存储(如动态内存)来分配其包含的值。
测试
我测试的完整代码在这个编译器资源管理器工作表上,尽管我不知道它还有多少时间。这就是为什么我将在这里描述我所做的测试。
我在测试什么
我正在做两个测试,每个测试都有一个功能:
- 检查值是否存在
- 检查值是否存在,如果存在则检索,否则检索67780
我使用-O2
minimum和--std=c++17
作为gcc 7.2和clang 5.0.0的编译器标志,用于x86_64目标。这里复制的结果来自clang。
使用std::optional
检查
代码:
bool check(const std::optional<int> maybe_int) {
return maybe_int.has_value();
}
结果:
mov al, byte ptr [rdi + 4]
ret
一种间接方式。
正在检索
代码:
int retrieve(const std::optional<int> maybe_int) {
if(maybe_int.has_value())
return maybe_int.value();
else
return 67780;
}
结果:
cmp byte ptr [rdi + 4], 0
je .LBB1_1
mov eax, dword ptr [rdi]
ret
.LBB1_1:
mov eax, 67780
ret
一个用于检查,一个用于检索。
使用自定义类
班级
template<typename T>
class my_optional {
private:
T val;
bool has_val;
public:
/* Constuctors ... */
bool has_value() const {
return has_val;
}
decltype(auto) value() const {
return val;
}
};
检查
代码:
bool check(const my_optional<int> maybe_int) {
return maybe_int.has_value();
}
结果:
shr rdi, 32
test dil, dil
setne al
ret
无间接性。
正在检索
代码:
int retrieve(const my_optional<int> maybe_int) {
if(maybe_int.has_value())
return maybe_int.value();
else
return 67780;
}
结果:
movabs rax, 1095216660480
test rdi, rax
mov eax, 67780
cmovne eax, edi
ret
虽然我不知道这是怎么回事,但它没有任何间接含义。
问题
标题或">我的测试出了什么问题?">
实现不允许使用额外的存储,如动态内存来分配其包含的值。
我读到:他们不允许在std::optional
的基础上使用额外的存储。但是std::optional<int>
看起来被实现为struct { int value; byte has_value; };
,并作为代码的引用(指针)传递(从机器代码的角度来看,它在C++术语中是"按值传递",但CPU的实际实现无论如何都使用一个间接级别),因此对内容的任何访问都需要该引用/指针的间接访问。
但是,如果您将其从空设置为值,或者删除值,它将不会更改分配的存储,它将在任何情况下保留这5个字节的存储(可能填充为8)。
您的自定义类确实使用了8字节的存储空间,将其作为整个qword
进行处理,通过算术(移位和掩蔽)提取has_value
和value
,看起来更好,在15秒内无法想到针对std
变体的特定缺点。
它是作为值而不是引用传递的。
如果std::optional<int>
的存储只有4个字节,并且它将自己作为指向动态分配的内存的指针,即空的,它将包含nullptr
,并且在将int
值存储到其中时,它将在某个地方分配动态内存,并且原始4B存储将保持指向新内存的指针。
- 1d 智能指针不适用于语法 (*)++
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 为什么使用 "this" 指针调用派生成员函数?
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用指针从C++中的数组中获取最大值
- 助记符和指向成员语法的指针
- 嵌入方指针压缩已禁用
- 数组的指针从不分段故障
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 何时在引用或唯一指针上使用移动语义
- QMetaObject invokeMethod的基于函数指针的语法
- 为什么std::optional有类似指针的开销
- 与C 中的普通指针相比,智能指针的开销是多少
- 复制开销:双精度与指针
- 声明对指针的指向者值的引用的开销
- 如何在容器中使用引用来避免指针的取消引用开销
- 与将标准作为模板参数传递给sort()相比,将标准函数指针传递给qsort()的开销更小
- C++智能指针取消引用 - 检查它是否已初始化的开销是多少?
- 指针解引用开销vs分支/条件语句
- 在vector中存储boost_shared指针——是否开销大?