std::launder 示例有以下代码块:
int x2[2][10];
auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0]));
// Undefined behavior: x2[1] would be reachable through the resulting pointer to x2[0]
// but is not reachable from the source
什么?std::aligned_storage
的例子在我看来并非如此:
std::aligned_storage_t<sizeof(T), alignof(T)> data[N];
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// Note: std::launder is needed after the change of object model in P0137R1
return *std::launder(reinterpret_cast<const T*>(&data[pos]));
}
这就是混淆的地方:&data[pos]
只是&data[pos][0]
,因为&p
==&p[0]
p
是一个数组。
如果std::aligned_storage
除了像alignas(T) std::byte[sizeof (T)];
这样的东西之外几乎不能以任何其他方式实现,这怎么会没关系?结构中的数组是否以某种方式神奇地使其正常?为什么?
假设我有
template <typename T>
using uninitialized = alignas(T) std::byte[sizeof (T)];
constexpr auto N = 5;
uninitialized<int> array[N];
for (std::size_t i = 0; i < N; i++) {
new (&array[i]) int(i);
}
现在怎么办?不是任何演员都像
auto laundered_value_ptr = std::launder(reinterpret_cast<int*>(&array[i]));
与第一个例子相同?这个呢:
auto laundered_array_ptr = std::launder(reinterpret_cast<int*>(array));
laundered_array_ptr[0] = 9;
laundered_array_ptr[2] = 76;
如果我遵循,似乎没有办法正确使用此内存,因为使用std::byte(*)[sizeof (int)]
意味着基本上可以访问它周围的任何东西,并且按照第一个示例,这个问题中写的所有内容也可能是 UB。
我用g++ -g -O0 -Wextra -Wall -Wpedantic -std=c++20 -fsanitize=undefined -fsanitize=address
编译了这些例子,奇怪的是,我甚至没有得到一个让我完全难倒的警告。
我怀疑这根本不重要,但这是我正在使用的编译器版本。
g++ (GCC) 12.1.1 20220730
你写道:
&data[pos]
只是&data[pos][0]
,因为&p
==&p[0]
其中p
是一个数组。
事实上,事实并非如此。如果p
是一个数组(调用其类型T[N]
),表达式&p == &p[0]
将不会编译。左侧有类型T(*)[N]
,右侧有类型T*
。这些不能直接比较,就像您可以将int*
与char*
进行比较一样。
但是,static_cast<void*>(&p) == static_cast<void*>(&p[0])
将是真的。
这两个指针表示相同的地址,但它们不能相互替换。
&data[pos]
是指向data
的一个元素的指针,而data
的所有其他元素都可以通过它到达。另一方面,(假设std::aligned_storage_t
是unsigned char
数组的别名)&data[pos][0]
只指向data
元素的单个元素;它不指向data
的整个元素。因此,data[pos]
的所有其他元素都可以通过它到达,但data
的其他元素则不能。
由于data
的所有元素都可以通过&data[pos]
访问,因此data
中的所有字节都可以通过&data[pos]
访问。将此指针转换为const T*
后,存储在data
的其他元素中的其他T
也可以从生成的指针访问,但是这些T
的字节也可以从原始指针访问,因此满足std::launder
的前提条件。
如果我们使用&data[pos][0]
作为reinterpret_cast
的参数,则只有单个元素data[pos]
中的字节可以从该指针访问,并且不满足前提条件。