我正在尝试创建一个静态容器,它具有基于堆栈的内存并且可以容纳N个实例.很多相似std::vector我希望当前未使用的内存不包含T的初始化项目.这通常通过新的放置来解决但是不可能在constexpr.
使用工会 我发现了一个技巧,你可以使用一个联合如下:
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;
};
Run Code Online (Sandbox Code Playgroud)
这使您可以通过设置empty成员来存储未初始化的项目,这可以解决constexpr中所有成员必须初始化的限制.
现在这种方法的问题在于,如果value_type是实现的类型,工会operator=的规则说:
如果一个union包含一个带有非平凡特殊成员函数的非静态数据成员(复制/移动构造函数,复制/移动赋值或析构函数),那么该函数在联合中默认被删除,需要由程序员.
这意味着为了能够使用这个技巧,我需要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;
}
Run Code Online (Sandbox Code Playgroud)
尝试#1:这似乎不可能,因为编译器抱怨在常量表达式中根本不允许更改union的活动成员.尝试#2:这适用于set()前一个代码段的方法,因为它不会更改活动成员本身,但会重新分配整个联合.这个技巧似乎无法在赋值运算符中使用,因为这会导致无休止的递归...
我在这里遗漏了什么,或者在constexpr中使用工会作为一种新的替代方案,这真的是一个死胡同吗?
还有其他替代方案,我完全错过了新的位置吗?
https://godbolt.org/z/km0nTY说明问题的代码
在C++ 17中,你不能.
该电流限制什么,你不能在常数表达式操作包括:
赋值表达式([expr.ass])或赋值操作符([class.copy.assign])的调用,它将改变联合的活动成员;
一个新的表达式 ;
真的没有办法解决这个问题.
在C++ 20中,您将能够,但可能不是您的想法.由于P0784的结果,后者的限制将在C++ 20中放宽:
- 除非所选分配函数是可替换的全局分配函数(21.6.2.1,21.6.2.2),否则为新表达式(8.3.4);
也就是说,new T会变好,但new (ptr) T仍然不允许.作为std::vector constexpr友好的一部分,我们需要能够管理"原始"内存 - 但我们仍然无法真正管理真正的原始内存.一切仍然需要打字.处理原始字节不起作用.
但std::allocator并不完全处理原始字节.allocate(n)给你一个T*和construct需要T*在该位置作为位置和一堆参数,并创建一个新对象.你可能想知道在这一点上这与新的位置有什么不同 - 唯一的区别是坚持std::allocator,我们留在这片土地T*- 但放置新的用途void*.这种区别变得至关重要.
不幸的是,这有你的constexpr版本"分配"内存的有趣结果(但它分配编译器内存,必要时会升级到静态存储 - 所以这可以做你想要的) - 但你的纯运行时版本肯定不想分配记忆,事实上,重点是它没有.为此,您必须使用is_constant_evaluated()在常量评估时分配和在运行时分配非分配.这当然不是很漂亮,但应该有效.
| 归档时间: |
|
| 查看次数: |
378 次 |
| 最近记录: |