当前位置: 首页 > 知识库问答 >
问题:

放置新的和完美的转发

梁才
2023-03-14

我有以下代码打算创建一个数组,但没有对其对象进行默认初始化。我想完美地前进到placement new,这似乎发生了,但我发现对象的析构函数在emplace函数中被调用。

#include <iostream>
#include <memory> // std::uninitialized_copy, std::allocator...
#include <utility> // std::move...
#include <bitset>


struct Int {

    int i;

    Int ( ) : i ( -1 ) { std::cout << "default constructed\n"; }
    Int ( const int i_ ) : i ( i_ ) { std::cout << i << " constructed\n"; }
    Int ( Int && int_ ) : i ( std::move ( int_.i ) ) { std::cout << i << " move constructed\n"; }
    Int ( const Int & int_ ) : i ( int_.i ) { std::cout << i << " copy constructed\n"; }
    ~Int ( ) { std::cout << i << " destructed\n"; i = -1; }
};


template <typename T, size_t S = 64>
class NoInitArray {

    std::bitset<S> m_used;

    T *m_array = reinterpret_cast < T* > ( ::operator new ( sizeof ( T ) * S ) );

public:

    T const &operator [ ] ( const size_t idx_ ) const {

        return m_array [ idx_ ];
    }

    NoInitArray ( ) { }

    ~NoInitArray ( ) {

        for ( size_t idx = 0; idx < S; ++idx ) {

            if ( m_used [ idx ] ) {

                reinterpret_cast< const T* > ( m_array + idx )->~T ( );
            }
        }
    }

    template<typename ...Args>
    void emplace ( const size_t idx_, Args &&... value_ ) {

        std::cout << "start emplace\n";

        m_used [ idx_ ] = 1;

        new ( m_array + idx_ ) T ( std::forward<T> ( value_ ) ... );

        std::cout << "end emplace\n";
    }
};


int main ( ) {

    NoInitArray<Int> nia;

    nia.emplace ( 0, 0 );
    nia.emplace ( 1, 1 );

    std::cout << nia [ 1 ].i << std::endl;

    nia.emplace ( 2, 2 );

    return 0;
}

运行该程序的结果如下:

start emplace
0 constructed
0 move constructed
0 destructed
end emplace
start emplace
1 constructed
1 move constructed
1 destructed
end emplace
1
start emplace
2 constructed
2 move constructed
2 destructed
end emplace
0 destructed
1 destructed
2 destructed

它表明对象被构造一次并被销毁两次(显然是UB),一次在emplace函数中,然后一次在NoInitArray的销毁时。

问题是“为什么Int对象的析构函数在emplace函数中调用?”?

编译器,Windhoze上最新的Clang/LLVM。

EDIT1:我在Int结构中添加了move和copy构造函数,现在计数匹配,即2个构造和2个破坏。

EDIT2:将放置新行从新(m\u array idx\uT(std::forward)更改为

EDIT3:只是为了将来的读者。如上所述,~NoInitArray()会泄漏内存。在m_array调用删除是个坏消息,而且这会调用(在Clang/LLVM中)m_array[0]的析构函数(但据我现在所知,这并不能保证,即UB)。std::malloc/std::free似乎是一条路,但有人说如果你这样做,所有的地狱都会崩溃,失去一条腿。

共有2个答案

韦高阳
2023-03-14

我认为构造函数和析构函数是在步骤:std::forward中调用的

标准:转发

能逸清
2023-03-14

“它表明对象被构造一次并被销毁两次”不是真的。输出X移动构造应包含为一个构造,因此构造是两次。

线路

new ( m_array + idx_ ) T ( std::forward<T> ( value_ ) ... );

应该是

new ( m_array + idx_ ) T ( std::forward<Args&&> ( value_ )... );

<代码>标准::转发

编辑

在你的编辑2中,你替换了没有std::forward的行。在这种情况下,好的,但是当你像这样调用emplace时,差异就出现了

    nia.emplace ( 0, Int(0) );

如果没有std::forwardnew T(value_...)调用复制构造函数,而new T(std::forward

编辑2

它应该是新的T(标准::转发

 类似资料:
  • C++11最显眼的功能之一就是完美转发功能。完美转发,太棒了!哎,开始使用,你就发现“完美”,理想与现实还是有差距。C++11的完美转发是非常好用,但是只有当你愿意忽略一些失败情况,这个Item就是使你熟悉这些情形。 在我们开始epsilon探索之前,有必要回顾一下“完美转发”的含义。“转发”仅表示将一个函数的参数传递给另一个函数。对于被传递的第二个函数目标是收到与第一个函数完全相同的对象。这就排

  • 移动语义使编译器可以用开销较低的移动操作替换昂贵的拷贝操作(但不是所有情况下移动都会比拷贝快),是 move-only 类型对象的支持基础 完美转发可以将某个函数模板的实参转发给其他函数,转发后的实参保持完全相同的值类型(左值、右值) 右值引用是移动语义和完美转发的实现基础,它引入了一种新的引用符号(&&)来区别于左值引用 这些名词很直观,但概念上容易与名称类似的函数混淆 移动操作的函数要求传入的

  • 正如michaelpark所指出的,如果我们不想最终使用错误的构造函数,那么添加完美的转发构造函数可能会很棘手。 目前,我有一个类,它使用了一个完美的转发构造函数,因此,我需要显式声明4个构造函数:

  • 当你第一次学习move语义和完美转发时,它们看起来很直截了当: Move语义使编译器能够把昂贵的拷贝操作替换为代价较小的move操作。 和拷贝构造函数以及拷贝赋值运算符能赋予你控制拷贝对象的能力一样,move构造函数以及move赋值运算符提供给你对move语义的控制。Move语义使得move-only类型的创建成为可能,比如说std::unique_ptr,std::future以及std::th

  • 这有点奇怪,我遵循Google places API为我的android应用程序添加了自动完成功能。谷歌为Android自动完成放置API请求拒绝,即使使用正确的API键。我甚至尝试与JSON客户机进行检查,并请求GET/POST仍然是相同的错误,因为我确信我的代码遵循了相应的google api自动完成集成。我还没有找到解决错误的任何解决方案。一些答案建议删除带有place_id的传感器。我不知

  • pre { white-space: pre-wrap; } 本教程向您展示如何使 HTML 元素可拖动,在本例中,我们将创建三个 DIV 元素然后启用他们的拖动和放置。 首先,我们创建三个 <div> 元素:     <div id="dd1"></div>     <div id="dd2"></div>     <div id="dd3"></div> 对于第一个 <div> 元素,我们