交换包含非平凡可复制类型的 std::aligned_storage 实例 - 未定义的行为?
Swapping `std::aligned_storage` instances containing non-trivially-copyable types - undefined behavior?
#include <iostream>
#include <type_traits>
using namespace std;
// Non-trivially-copyable type.
struct NTC
{
int x;
NTC(int mX) : x(mX) { }
~NTC() { cout << "boop." << x << endl; }
};
int main()
{
using AS = aligned_storage_t<sizeof(NTC), alignof(NTC)>;
// Create two `std::aligned_storage` instances
// and "fill" them with two "placement-new-constructed"
// `NTC` instances.
AS as1, as2;
new (&as1) NTC{2};
new (&as2) NTC{5};
// Swap the `aligned_storages`, not their contents.
std::swap(as1, as2);
// Explicitly call `~NTC()` on the contents of the
// aligned storage instances.
NTC& in1{*static_cast<NTC*>(static_cast<void*>(&as1))};
NTC& in2{*static_cast<NTC*>(static_cast<void*>(&as2))};
in1.~NTC();
in2.~NTC();
return 0;
}
上面的代码是未定义的行为吗?
这是我认为正在发生的事情:
NTC
是一种非平凡可复制的类型。
- 我正在创建两个适合存储
NTC
个对象 (std::aligned_storage
) 的内存位置。
- 我将两个
NTC
实例直接构造到内存位置。
std::aligned_storage
instances are PODTypes。
This means the type is compatible with the types used in the C programming language, can be manipulated using C library functions: it can be created with std::malloc, it can be copied with std::memmove, etc, and can be exchanged with C libraries directly, in its binary form.
- 由于对齐的存储实例是 POD 类型,我应该被允许 move/swap/copy 它们。
- 交换对齐存储实例意味着从对齐存储A中取出所有字节并将它们与对齐存储B中的所有字节交换。
- 这样做不会调用内部存储的
NTC
个对象的 destructor/copy-constructor。
我的观点是否不正确?如果确实发生了未定义的行为,它发生在程序的哪一部分?为什么?
新增potentially-correct/incorrect信息(gathered from a deleted answer):
std::aligned_storage
几乎只是 C 风格数组的类型定义。
std::swap
has an overload for C-style arrays since C++11.
- 重载 calls
std::swap_ranges
,它交换数组中的每个元素。
- 因此,交换两个
std::aligned_storage
实例应该逐个元素地交换它们的内容。
我在这些新假设中有什么错误吗?
在将不可平凡复制的类型放入缓冲区后直接访问缓冲区的字节是一个非常糟糕的主意,但还不是未定义的。
作为 NTC
交换后尝试访问缓冲区违反了别名规则,[basic.lval]p10:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
(10.1) -- the dynamic type of the object,
[....]
通过 memcpy
或等价物复制可简单复制的类型意味着保留动态类型。对于非平凡可复制的类型没有这样的暗示,所以在交换之后,你不再有任何 NTC
对象可以访问。
#include <iostream>
#include <type_traits>
using namespace std;
// Non-trivially-copyable type.
struct NTC
{
int x;
NTC(int mX) : x(mX) { }
~NTC() { cout << "boop." << x << endl; }
};
int main()
{
using AS = aligned_storage_t<sizeof(NTC), alignof(NTC)>;
// Create two `std::aligned_storage` instances
// and "fill" them with two "placement-new-constructed"
// `NTC` instances.
AS as1, as2;
new (&as1) NTC{2};
new (&as2) NTC{5};
// Swap the `aligned_storages`, not their contents.
std::swap(as1, as2);
// Explicitly call `~NTC()` on the contents of the
// aligned storage instances.
NTC& in1{*static_cast<NTC*>(static_cast<void*>(&as1))};
NTC& in2{*static_cast<NTC*>(static_cast<void*>(&as2))};
in1.~NTC();
in2.~NTC();
return 0;
}
上面的代码是未定义的行为吗?
这是我认为正在发生的事情:
NTC
是一种非平凡可复制的类型。- 我正在创建两个适合存储
NTC
个对象 (std::aligned_storage
) 的内存位置。 - 我将两个
NTC
实例直接构造到内存位置。 std::aligned_storage
instances are PODTypes。This means the type is compatible with the types used in the C programming language, can be manipulated using C library functions: it can be created with std::malloc, it can be copied with std::memmove, etc, and can be exchanged with C libraries directly, in its binary form.
- 由于对齐的存储实例是 POD 类型,我应该被允许 move/swap/copy 它们。
- 交换对齐存储实例意味着从对齐存储A中取出所有字节并将它们与对齐存储B中的所有字节交换。
- 这样做不会调用内部存储的
NTC
个对象的 destructor/copy-constructor。
我的观点是否不正确?如果确实发生了未定义的行为,它发生在程序的哪一部分?为什么?
新增potentially-correct/incorrect信息(gathered from a deleted answer):
std::aligned_storage
几乎只是 C 风格数组的类型定义。std::swap
has an overload for C-style arrays since C++11.- 重载 calls
std::swap_ranges
,它交换数组中的每个元素。 - 因此,交换两个
std::aligned_storage
实例应该逐个元素地交换它们的内容。
我在这些新假设中有什么错误吗?
在将不可平凡复制的类型放入缓冲区后直接访问缓冲区的字节是一个非常糟糕的主意,但还不是未定义的。
作为 NTC
交换后尝试访问缓冲区违反了别名规则,[basic.lval]p10:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
(10.1) -- the dynamic type of the object,
[....]
通过 memcpy
或等价物复制可简单复制的类型意味着保留动态类型。对于非平凡可复制的类型没有这样的暗示,所以在交换之后,你不再有任何 NTC
对象可以访问。