c++ - Constexpr 替代放置 new 以使内存中的对象未初始化?
问题描述
我正在尝试创建一个静态容器,它具有基于堆栈的内存并且可以容纳 T 的 N 个实例。非常相似std::vector
,我希望当前未使用的内存不包含 T 的初始化项。这通常通过放置 new 来解决,但这不可能在常量表达式。
使用联合 我发现了一个技巧,您可以为此使用联合,如下所示:
template <typename value_type>
union container_storage_type
{
struct empty{};
constexpr container_storage_type(): uninitialized{}{}
constexpr container_storage_type(value_type v): value(v){}
constexpr void set(value_type v)
{
*this = literal_container_storage_type{v};
}
empty uninitialized;
value_type value;
};
这使您可以通过设置empty
成员来存储未初始化的项目,这可以解决 constexpr 中的所有成员都必须初始化的限制。
现在这种方法的问题在于,如果value_type
是一个实现的类型operator=
,则联合规则说:
如果联合包含具有非平凡特殊成员函数(复制/移动构造函数、复制/移动赋值或析构函数)的非静态数据成员,则该函数默认在联合中被删除,并且需要由程序员。
这意味着为了能够使用这个技巧,我也需要operator=
在联合中实现,但是看起来怎么样?
constexpr container_storage_type& operator=(const container_storage_type& other)
{
value = other.value; //ATTEMPT #1
//*this = container_storage_type(other.value);ATTEMPT #2
return *this;
}
尝试#1:这似乎不可能,因为编译器抱怨在常量表达式中根本不允许更改联合的活动成员。尝试 #2:这适用于set()
上一个代码段中的方法,因为它本身不会更改活动成员,但会重新分配整个联合。这个技巧似乎无法在赋值运算符中使用,但是因为这会导致无休止的递归......
我在这里遗漏了什么,或者这真的是在 constexpr 中使用联合作为新布局替代方案的死胡同吗?
除了我完全错过的新安置之外,还有其他选择吗?
https://godbolt.org/z/km0nTY说明问题的代码
解决方案
在 C++17 中,你不能。
当前对常量表达式中不能执行的操作的限制包括:
赋值表达式 ([expr.ass]) 或赋值运算符 ([class.copy.assign]) 的调用将更改联合的活动成员;
一个新的表达式;
真的没有办法解决这个问题。
在 C++20 中,您将能够,但可能不是您想的那样。由于P0784 ,后一个限制将在 C++20 中放宽为:
- 新表达式(8.3.4),除非所选分配函数是可替换的全局分配函数 (21.6.2.1, 21.6.2.2);
也就是说,new T
会变得很好,但new (ptr) T
仍然不允许。作为使std::vector
constexpr
-friendly 的一部分,我们需要能够管理“原始”内存——但我们仍然不能真正管理真正的原始内存。一切仍然需要输入。处理原始字节是行不通的。
但std::allocator
并不完全处理原始字节。allocate(n)
为您提供 aT*
并将construct
aT*
作为位置和一堆参数,并在该位置创建一个新对象。此时您可能想知道,这与放置新的有什么不同——唯一的区别是std::allocator
,我们坚持使用,T*
但放置新的用途void*
。事实证明,这种区别很关键。
不幸的是,这有一个有趣的结果,即您的constexpr
版本“分配”内存(但它分配编译器内存,这将在必要时提升到静态存储 - 所以这可以满足您的需求) - 但您的纯运行时版本肯定不想分配记忆,事实上,重点是它没有。为此,您将不得不使用is_constant_evaluated()
在恒定评估时间分配和运行时不分配之间切换。诚然,这并不漂亮,但它应该可以工作。
推荐阅读
- ssms - 无法在 SQL Server Management Studio 中更改查询类型
- javascript - CKeditor 5文本区域未更新
- c# - 在编辑 System.NullReferenceException 时使用 DataTable 绑定数据网格:“对象引用未设置为对象的实例。”
- image - 使用 cv2 在 atom 中打印图像时导入错误
- utf-8 - Tableau 服务器 - 雪花 (50311) UTF-8 错误
- windows - 删除 Sublime Text 中的重复行但不删除空行
- javascript - 通过链接 querySelector 选择元素的 jQuery 替代方法
- c# - TicTacToe if 语句不适用于玩家 2
- python - 使用 NLTK 的 PDF 摘要
- api - 如何在 RESTAssure API 自动化测试中创建和使用全局变量