代码之家  ›  专栏  ›  技术社区  ›  Daniel Langr

为什么没有一个STD::C++中的CuthToGy?

  •  55
  • Daniel Langr  · 技术社区  · 6 年前

    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);
    
    5 回复  |  直到 6 年前
        1
  •  43
  •   Nicol Bolas    4 年前

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

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

      当然,我们都喜欢把它包在一个袋子里 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 . 在这两种情况下,都必须说明要创建的类型。在这两种情况下都必须提供构造函数的参数。在这两种情况下都必须提供指向内存的指针。

    .

    这是客观的 能力较弱 而不是新的。您可以这样做:

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

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

    你的假设 不能 允许你选择你想要的。如果您不提供参数,它可以有执行默认初始化的代码,但是它将无法提供值初始化的版本。它可以不带参数地初始化值,但是不能默认初始化对象。


    标准::构造 . 但它这样做的原因不是一致性。它们支持编译时内存分配和构造。

    你可以称之为“可替换的”全局 新的 已替换

    constexpr分配方案的早期版本 relied on std::allocator_traits<std::allocator<T>>::construct/destruct . 后来他们搬到了伦敦 标准::构造 作为 constexpr 构造函数 construct 指的是。

    所以呢 construct_at

        2
  •  13
  •   muru Rider44    6 年前

    有这么一件事,但是 not named like you might expect

    • 未初始化的\u拷贝 将一系列对象复制到未初始化的内存区域

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

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

    • 未初始化的\u填充\u n (功能模板)
    • 未初始化的移动 (C++ 17) 将一系列对象移动到未初始化的内存区域 (功能模板)
    • (C++ 17) 将多个对象移动到未初始化的内存区域 (功能模板)
    • (C++ 17) (功能模板)
    • 未初始化的\u默认\u构造\u n (C++ 17) 默认情况下,在未初始化的内存区域(由开始和计数定义)初始化时构造对象 (功能模板)
    • (C++ 17) 通过值初始化在未初始化的内存区域(由范围定义)中构造对象 (功能模板)
    • (C++ 17) 在未初始化的内存区域中通过值初始化构造对象,该内存区域由开始和计数定义
        3
  •  11
  •   David Stone    6 年前

    std::construct_at 已经添加到C++ 20中。 More constexpr containers

    添加此功能的建议的目的是支持constexpr内存分配,包括 std::vector void * ,不是 T * . constexpr 标准::构造 constexpr T * construct_at(T *, Args && ...) .

    这还具有不要求用户指定正在构造的类型的优点;它是根据指针的类型推导出来的。正确地调用placement new的语法有点可怕,而且违反直觉。比较 std::construct_at(ptr, args...) ::new(static_cast<void *>(ptr)) std::decay_t<decltype(*ptr)>(args...) .

        4
  •  9
  •   Konrad Rudolph    6 年前

    std::allocator_traits::construct . 以前这里还有一个 std::allocator ,但那被删除了,在 standards committee paper D0174R0

        5
  •  1
  •   alfC    6 年前

    我认为应该有一个标准的构造函数。 事实上libc++在文件中有一个实现细节 stl_construct.h

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

    我认为这是有用的东西,因为它可以使“新的位置”的朋友。 uninitialized_copy 进入默认堆(从 std::initializer_list 元素。)


    我有自己的容器库,可以重新实现 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)...);
        }
    }
    

    它被声明为move only类的友元,只允许在放置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;
        ...
    };
    
        6
  •  0
  •   Peter Mortensen icecrime    6 年前

    construct

    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 在图书馆可以完成标准的图书馆词汇。