首页 > 解决方案 > 为什么 C++17 中没有 std::construct_at ?

问题描述

C++17 增加了std::destroy_at,但没有std::construct_at对应的。这是为什么?难道不能像下面这样简单地实现吗?

template <typename T, typename... Args>
T* construct_at(void* addr, Args&&... args) {
  return new (addr) T(std::forward<Args>(args)...);
}

这将能够避免这种不完全自然的放置新语法:

auto ptr = construct_at<int>(buf, 1);  // instead of 'auto ptr = new (buf) int(1);'
std::cout << *ptr;
std::destroy_at(ptr);

标签: c++c++17placement-new

解决方案


std::destroy_at与直接析构函数调用相比,提供了两个客观的改进:

  1. 它减少了冗余:

     T *ptr = new T;
     //Insert 1000 lines of code here.
     ptr->~T(); //What type was that again?
    

    当然,我们都更愿意将它包装在 a 中unique_ptr并完成它,但如果由于某种原因不能做到这一点,那么就T存在冗余元素。如果我们将类型更改为U,我们现在必须更改析构函数调用,否则会中断。使用std::destroy_at(ptr)消除了在两个地方更改相同内容的需要。

    干是好的。

  2. 它使这很容易:

     auto ptr = allocates_an_object(...);
     //Insert code here
     ptr->~???; //What type is that again?
    

    如果我们推断出指针的类型,那么删除它就变得有点困难了。你不能这样做ptr->~decltype(ptr)();因为 C++ 解析器不能那样工作。不仅如此,decltype将类型推断为指针,因此您需要从推断的类型中删除指针间接。带领您:

     auto ptr = allocates_an_object(...);
     //Insert code here
     using delete_type = std::remove_pointer_t<decltype(ptr)>;
     ptr->~delete_type();
    

    谁想打字

相比之下,您的假设std::construct_at没有提供比放置的客观new改进。在这两种情况下,您都必须说明您正在创建的类型。在这两种情况下都必须提供构造函数的参数。在这两种情况下都必须提供指向内存的指针。

所以没有必要被你的假设解决std::construct_at

它在客观上不如安置新。你可以这样做:

auto ptr1 = new(mem1) T;
auto ptr2 = new(mem2) T{};

这些是不同的。在第一种情况下,对象是默认初始化的,这可能会使其未初始化。在第二种情况下,对象是值初始化的。

你的假设std::construct_at 不能让你选择你想要的。如果您不提供任何参数,它可以具有执行默认初始化的代码,但是它将无法提供用于值初始化的版本。它可以在没有参数的情况下初始化值,但是你不能默认初始化对象。


请注意,C++20 添加了std::construct_at. 但它这样做是出于一致性以外的原因。它们在那里支持编译时内存分配和构造。

您可以在常量表达式中调用“可替换”全局new运算符(只要您实际上没有替换它)。但是placement-new 不是一个“可替换”的函数,所以你不能在那里调用它。

constexpr 分配提案的早期版本依赖于std::allocator_traits<std::allocator<T>>::construct/destruct. 他们后来转移到std::construct_at作为constexpr构造函数,这construct将是指。

construct_at当可以提供对placement-new 的客观改进时,添加了So。


推荐阅读