std::make_unique 与放置新

问题描述

我正在尝试定义一个包含其大小的 Data 对象,后跟 size 字节的数据。

类似于:

struct Data {
    size_t size;
    char data[1];
    
    static void* operator new( std::size_t size,size_t dataSize ) {
        return ::operator new( size+dataSize-1 );
    }
    static void operator delete( void* data ){
        ::operator delete( data );
    }
    
    Data( size_t size ) : size(size) {
        std::memset( data,size );
    }
};

这行得通,我可以用新的位置分配它:

Data* data = new (3) Data( 3 );

我想创建一个 std::unique_ptr<Data> 并且,as good practice,我更喜欢使用 std::make_uinque,而不是原始的 new

是否可以调用 std::make_unique<Data> 将数据传递给新位置?

任何版本的 C++ 标准都可以。

解决方法

没有标准方法可以做到这一点,因为标准 C++ 没有为 VLA/灵活数组成员提供任何真正的支持。 make_unique 专为以下任一个编写:

  1. 固定大小的单个对象(它没有提供有关“真实”大小的信息的机制,它只使用 new,它假定 sizeof T 是正确和完整的)
  2. 固定大小的对象数组(根据定义,数组必须具有固定大小的对象,否则索引不起作用)

我想理论上您可以制作自己的支持准灵活数组成员的版本,但是在 C++ 标准库中对此有支持。 std::make_unique 甚至没有分配器感知的变体;它不会竭尽全力支持不寻常的用例。 std::allocate_sharedstd::make_shared 的分配器感知版本)可能被迫以荒谬的方式支持这一点(您基本上需要编写自定义分配器,知道有多少额外的内存分配请求应该提供)和there's a proposal for an allocator-aware version of std::make_unique,但同样,让这项工作变得疯狂。

您说“作为一种好的做法,我更喜欢使用 std::make_unique,而不是原始的 new”,但是 C++ 中的灵活数组成员已经是不好的做法;对它们的支持基本上为零,因为它们打破了大部分 C++ 语言和库所依赖的各种假设。如果必须这样做,请编写自己的工厂方法来执行工作;当然,使用原始 new 是不赞成的,但将其限制在代码中的单个 API 中以保证结果是托管指针,然后再将其传递到该 API 之外会限制“损害”。