交换包含非平凡可复制类型的`std :: aligned_storage`实例 - 未定义的行为?

Vit*_*meo 2 c++ swap undefined-behavior language-lawyer c++14

意想不到的联系

#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;
}
Run Code Online (Sandbox Code Playgroud)

以上代码是否未定义行为?

这就是我认为发生的事情:

  • NTC 是一种非平凡的可复制类型.
  • 我正在创建两个适合存储NTC对象的内存位置(std::aligned_storage).
  • 我将两个NTC实例直接构造到内存位置.
  • std::aligned_storage实例是PODTypes.

    这意味着该类型与C编程语言中使用的类型兼容,可以使用C库函数进行操作:它可以使用std :: malloc创建,可以使用std :: memmove等进行复制,并且可以进行交换直接使用C库,采用二进制形式.

  • 由于对齐的存储实例是POD类型,我应该被允许移动/交换/复制它们.
  • 交换对齐的存储实例意味着从对齐的存储A中获取所有字节,并将它们与对齐存储B中的所有字节交换.
  • 这样做不会调用内部存储NTC对象的析构函数/复制构造函数.

我的任何一点都不正确吗?如果确实发生了未定义的行为,程序的哪个部分会发生?为什么?


新的可能正确/不正确的信息(从已删除的答案中收集):

我在这些新的假设中犯了什么错误吗?

小智 5

在将非平凡可复制类型放入其中之后直接访问缓冲区的字节是一个非常糟糕的想法,但尚未定义.

交换后尝试访问缓冲区NTC违反了别名规则,[basic.lval] p10:

如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:

(10.1) - 对象的动态类型,

[....]

通过memcpy或等效复制一个简单的可复制类型暗示保留动态类型.对于非平凡的可复制类型没有这样的含义,因此在交换之后,您不再有任何NTC对象可以访问.

  • @VittorioRomeo有关如何使用非平凡可复制类型破坏事物的一些实际示例,请考虑pimpl习惯用法,但请考虑实现类型由于某种原因需要返回指针的情况.如果像你一样交换缓冲区,这就无法工作,即使该类型没有任何虚函数. (2认同)