【问题标题】:std::for_each() and calling constructors/destructorsstd::for_each() 和调用构造函数/析构函数
【发布时间】:2015-09-07 15:08:12
【问题描述】:

我在使用 for_each() 并在每个元素上调用构造函数/析构函数时遇到问题。

作为参考,mBegin 指向数组的开头,mEnd 超出最后一个元素,mCapacity 指向分配内存的结尾。

template <typename T>
void IDMapTree<T>::Grow()
{
    const size_t prevSize = mCapacity - mBegin;
    const size_t newSize = prevSize != 0 ? static_cast<size_t>(1.5f * prevSize) : 1;
    T* newBuffer = static_cast<T*>(mAllocator.Allocate(newSize));

    // initialize new buffer elements with copy constructor using old elements
    uint32_t itemIndex = 0;
    std::for_each(newBuffer, newBuffer + prevSize, [&](T& item) { item.T(*(mBegin + itemIndex++)); });
    // destruct all old elements
    std::for_each(mBegin, mEnd, [](T& item) { item.~T(); });


    // ...
}

这段编译得很好:

std::for_each(mBegin, mEnd, [](T& item) { item.~T(); });

但这不是:

std::for_each(newBuffer, newBuffer + prevSize, [&](T& item) { item.T(*(mBegin + itemIndex++)); });

即使我像这样使用默认构造函数也不行:

std::for_each(newBuffer, newBuffer + prevSize, [](T& item) { item.T(); });

编译器 (VS2013) 说明如下:

error C2039: '__this' : is not a member of 'JonsEngine::SceneNode'
error C2039: 'T' : is not a member of 'JonsEngine::SceneNode'

在这种情况下,T 的类型为 JonsEngine::SceneNode

这里有什么问题?为什么T() 不解析为SceneNode()?为什么析构函数有效,构造函数无效?

【问题讨论】:

  • 您正在尝试调用已存在对象的构造函数? o.0
  • 为什么要显式调用析构函数?您是否使用placement new 构建了元素?
  • 只使用std::vector作为存储空间。
  • 使用placement new 是的,为什么不能使用for_each 来调用析构函数?
  • 出于此问题范围之外的原因,我不能只使用向量。如果陈述有误,请详细说明?

标签: c++ c++11 stl


【解决方案1】:

您可以为此使用std::uninitialized_copy()

【讨论】:

    【解决方案2】:

    像 stl 那样做 - 将未初始化的内存块作为 void* ptr 传递,然后

    ::operator new(ptr) T(item)

    item 应作为 const 引用传递。 (或更好 - 移动)

    char* newBuffer = static_cast<char*>(mAllocator.Allocate(newSize));
    if (newSize % sizeof(T) != 0){
      throw std::runtime_error("memory is not alligned");
    }
    for (size_t i = 0; i< newSize; i += sizeof(T)){
       ::operator new((void*)ptr) T(item)
    }
    

    您还应该移动对象,而不是复制和删除旧对象。 这也将使您无需销毁旧对象。

    编辑: 当这是在预分配的内存块上调用任何构造函数的正确方法时,我不明白为什么会投反对票。这是做同样事情的 MSVC++ Allocator::construct 函数

    void construct(_Ty *_Ptr, const _Ty& _Val)
        {   // construct object at _Ptr with value _Val
        ::new ((void *)_Ptr) _Ty(_Val);
        } 
    

    还想否决 MSVC++ 声誉吗?

    【讨论】:

    • 他显然在做某种(奇怪的)分配器。想想好像这段代码在 allocator::construct 中,突然间我们就不会那么消极了。
    • 另外,当您甚至不知道 OP 试图解决什么问题时,我不明白您如何判断什么是好的解决方案
    【解决方案3】:

    你只需要使用placement new。

    顺便说一句,你使用析构函数很好:

    // destruct all old elements
    std::for_each(mBegin, mEnd, [](T& item) { item.~T(); });
    

    表达式item.~T(); 是显式调用析构函数的正确方法。

    你不能这样调用构造函数。

    所以,新位置可能是要走的路:

    std::for_each(newBuffer, newBuffer + prevSize, [](T& item) { new (&item) T(); });
    

    顺便说一句,在成员函数中使用 lambda 时,您可能需要将 this 传递到捕获列表中。

    【讨论】:

      【解决方案4】:

      为什么析构函数有效,构造函数无效?

      因为析构函数属于实例,构造函数属于类型。

      您不能调用具有实例的构造函数,您必须使用类型的名称来调用它。不管怎样,你在做一些奇怪的事情。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-16
        • 2021-07-28
        • 1970-01-01
        • 2014-03-13
        • 2013-07-07
        • 2013-06-24
        相关资源
        最近更新 更多