我和一个比我聪明的人进行了一次有趣的讨论,我仍然有一个关于对齐存储和可复制/可破坏类型的开放式问题。
考虑下面的例子:
#include <type_traits>
#include <vector>
#include <cassert>
struct type {
using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
using fn_type = int(storage_type &);
template<typename T>
static int proto(storage_type &storage) {
static_assert(std::is_trivially_copyable_v<T>);
static_assert(std::is_trivially_destructible_v<T>);
return *reinterpret_cast<T *>(&storage);
}
std::aligned_storage_t<sizeof(void *), alignof(void *)> storage;
fn_type *fn;
bool weak;
};
int main() {
static_assert(std::is_trivially_copyable_v<type>);
static_assert(std::is_trivially_destructible_v<type>);
std::vector<type> vec;
type t1;
new (&t1.storage) char{'c'};
t1.fn = &type::proto<char>;
t1.weak = true;
vec.push_back(t1);
type t2;
new (&t2.storage) int{42};
t2.fn = &type::proto<int>;
t2.weak = false;
vec.push_back(t2);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto &t) { return t.weak; }), vec.end());
assert(vec.size() == 1);
assert(!vec[0].weak);
assert(vec[0].fn(vec[0].storage) == 42);
}
这是真实世界案例的简化版本。我真的希望我没有犯错误或简化它太多。
正如您所看到的,这个想法是存在一种类型,称为
type
(你知道,命名事物很难)有三个数据成员:
-
storage
sizeof(void *)
-
fn
指向具有类型的函数的指针
int(storage_type &)
-
weak
一个只用于介绍示例的无用的bool
创建的新实例的步骤
类型
main
函数),我输入一个值(或
int
或者
char
)在存储区域和静态函数模板的右侧专门化
proto
在里面
fn
.
以后,当我想
援引
得到它返回的整数值,我做如下操作:
int value = type_instance.fn(type_instance.storage);
到目前为止,一切顺利。尽管存在风险和容易出错的事实(但这是一个示例,实际用例并非如此),但
.
注意
类型
int
和
烧焦
这个
(或者更好的是
怀疑
)当我将类型eg的实例放入向量中时出现(请参见
主要的
函数),并决定从阵列中移除其中一个,以便移动其他一些以保持其压缩。
更一般地说,当我想要复制或移动的实例时,我不再确定会发生什么
类型
不管是不是UB。
我的猜测是,它被允许是放在存储中的类型,可以简单地复制,也可以简单地破坏。另一方面,我被告知,这不是标准直接允许的,它可以被视为
,因为事实上几乎所有的编译器都允许您这样做(我可以保证这一点,看起来是这样的)
到处都可以找到一些关于
工作
所以,问题是:这是允许的还是不允许的?在第二种情况下,我能做些什么来解决这个问题?此外,C++20是否会因此而改变?