为什么在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);
Daniel Langr asked 2019-11-08T12:38:28Z
6个解决方案
40 votes

std::construct_at在直接析构函数调用方面提供了两个客观的改进:

  1. 它减少了冗余:

    std::construct_at

    当然,我们所有人都希望将其包装在std::construct_at中并完成它,但是如果由于某种原因无法做到这一点,则将new放入一个冗余元素。 如果我们将类型更改为U,则现在必须更改析构函数调用或事情中断。 使用std::destroy_at(ptr)无需在两个地方更改同一件事。

    干好。

  2. 这很容易:

    std::construct_at

    如果我们推断出指针的类型,那么删除它会变得很困难。 你不能做std::construct_at; 因为C ++解析器无法正常工作。 不仅如此,new将类型推导为指针,因此您需要从推导的类型中删除指针间接。 引导您:

    std::construct_at

    谁想输入?

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

因此,您的假设std::construct_at无需解决。

而且,从客观上讲,它的功能不如放置新产品。 你可以这样做:

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

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

假设的std::construct_at无法让您选择想要的那个。 如果不提供任何参数,它可以具有执行默认初始化的代码,但是它将无法提供用于值初始化的版本。 而且它可以不使用任何参数进行值初始化,但是您不能默认初始化该对象。

Nicol Bolas answered 2019-11-08T12:40:23Z
13 votes

有这样的事情,但没有您所期望的那样命名:

  • uninitialized_copy将一系列对象复制到未初始化的内存区域

  • uninitialized_copy_n(C ++ 11)将许多对象复制到未初始化的内存区域(功能模板)

  • uninitialized_fill将对象复制到未初始化的内存区域(由范围定义)(功能模板)

  • uninitialized_fill_n将对象复制到未初始化的内存区域,该内存由开始和计数定义(功能模板)
  • uninitialized_move(C ++ 17)将一系列对象移动到未初始化的内存区域(功能模板)
  • uninitialized_move_n(C ++ 17)将许多对象移至未初始化的内存区域(功能模板)
  • uninitialized_default_construct(C ++ 17)通过默认初始化在未初始化的内存区域(由范围定义)中构造对象(功能模板)
  • uninitialized_default_construct_n(C ++ 17)通过默认初始化在未初始化的内存区域中构造对象,该内存由开始和计数定义(功能模板)
  • uninitialized_value_construct(C ++ 17)通过值初始化在一个未定义的内存区域中构造对象,该区域由范围定义(功能模板)
  • uninitialized_value_construct_n(C ++ 17)通过值初始化在未初始化的内存区域中构造对象,该内存由开始和计数定义
Marek R answered 2019-11-08T12:42:03Z
9 votes

std::allocator。过去有std::allocator,但在标准委员会文件D0174R0中已将其删除。

Konrad Rudolph answered 2019-11-08T12:42:30Z
6 votes

std::construct_at已添加到C ++ 20。 这样做的文件是More constexpr容器。 据推测,与C ++ 17中的新增功能相比,这没有看到足够的优势,但是C ++ 20改变了一切。

建议添加此功能的目的是为了支持constexpr内存分配,包括std::construct_at(ptr, args...)。这要求能够将对象构造到分配的存储中。 但是,仅普通交易是根据::new(static_cast<void *>(ptr)) std::decay_t<decltype(*ptr)>(args...)而不是T *进行的新交易。constexpr评估当前无法访问原始存储,委员会希望保持这种方式。 库函数std::construct_at添加了类型化的接口constexpr T * construct_at(T *, Args && ...)

这还具有不需要用户指定正在构造的类型的优点。 它是根据指针的类型推导出来的。 正确地将放置位置称为“ new”的语法有点恐怖和违反直觉。 比较std::construct_at(ptr, args...)::new(static_cast<void *>(ptr)) std::decay_t<decltype(*ptr)>(args...)

David Stone answered 2019-11-08T12:43:19Z
0 votes

construct_at似乎没有提供任何语法糖。 而且,它的效率不如新布局。 绑定到引用参数会导致临时实现和额外的移动/复制构造:

struct heavy{
   unsigned char[4096];
   heavy(const heavy&);
};
heavy make_heavy(); // Return a pr-value
auto loc = ::operator new(sizeof(heavy));
// Equivalently: unsigned char loc[sizeof(heavy)];

auto p = construct<heavy>(loc,make_heavy()); // The pr-value returned by
         // make_heavy is bound to the second argument,
         // and then this arugment is copied in the body of construct.

auto p2 = new(loc) auto(make_heavy()); // Heavy is directly constructed at loc
       //... and this is simpler to write!

不幸的是,调用函数时没有任何方法可以避免这些额外的复制/移动构造。 转发几乎是完美的。

另一方面,库中的construct_at可以完成标准库词汇。

Oliv answered 2019-11-08T12:44:02Z
0 votes

我认为应该有一个标准的构造函数。实际上,libc ++在文件detail::uninitialized_copy中有一个作为实现细节。

namespace std{
...
  template<typename _T1, typename... _Args>
    inline void
    _Construct(_T1* __p, _Args&&... __args)
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
...
}

我认为拥有它很有用,因为它可以使“新布局”成为朋友。对于只需要移动到默认堆中的detail::uninitialized_copy的仅移动类型(例如,来自detail::construct元素),这是一个很好的定制点。


我有自己的容器库,可重新实现detail::uninitialized_copy(范围)以使用自定义detail::construct

namespace detail{
    template<typename T, typename... As>
    inline void construct(T* p, As&&... as){
        ::new(static_cast<void*>(p)) T(std::forward<As>(as)...);
    }
}

声明为仅移动类的朋友,该类仅允许在new放置的上下文中进行复制。

template<class T>
class my_move_only_class{
    my_move_only_class(my_move_only_class const&) = default;
    friend template<class TT, class...As> friend void detail::construct(TT*, As&&...);
public:
    my_move_only_class(my_move_only_class&&) = default;
    ...
};
alfC answered 2019-11-08T12:44:54Z
translate from https://stackoverflow.com:/questions/52966560/why-isnt-there-a-stdconstruct-at-in-c17