std::move 对堆栈变量有意义吗

问题描述 投票:0回答:3

我需要创建一个大小为 1024 的结构列表。因此,我尝试通过使用移动语义来优化对列表的推送操作。但移动语义仅对堆分配的内存有意义(据我所知)。有人可以建议我更好的方法吗?这是我的代码

#include <iostream>
#include <list>
    
struct St {
    int32_t arr[1024];
};
    
int main() {
    std::list<St> struct_list;
    for(int32_t i = 0; i < 1024; ++i) {
        St st; // stack allocated and memory gets deallocated after each loop
        std::cout << &st << std::endl;
        struct_list.emplace_back(std::move(st)); // I feel std::move doesn't make any sense here even if I implement move constructor, still data gets copied into the std::list which is allocated in heap, so not efficient.
        std::cout << &struct_list.back() << "\n" << std::endl;
    }
        
    return EXIT_SUCCESS;
}
c++ c++11
3个回答
6
投票

鉴于您当前对

St
的定义:

struct St {
    int32_t arr[1024];
};

从技术上讲,对于

St
的这个特定定义,移动和复制两个结果是相同的。

但是,从语义上讲,在完成后标记

st
进行移动确实有意义,并且
st
无论如何都会被销毁。例如,如果您稍后决定将
St
的定义更改为:

struct St {
    std::vector<int32_t> vec;
};

然后,它会产生影响 - 矢量数据成员将被移动而不是复制。

简而言之,我的建议是关注移动操作的语义 – 即,对于要移动的对象有意义吗?好吧,如果你无论如何都要处理该对象,那么它非常可能会。


2
投票

如果您想升级到C++17,您可以使用emplace_back的返回值首先将其添加到列表中,然后填充它。 您可以(如果默认可构造,如您的示例)首先 emplace_back 一个新实例,然后使用

.back()
获取它并填充它。

堆栈分配和释放并不昂贵,尤其是对于 POD 而言。重要的是你对它所做的逻辑。

关于移动,对于这个例子,std::move 的作用与复制相同,所以不,你不需要它。但是,如果这是一个

std::string
数组,则 std::move 将允许您将字符串从一个数组移动到另一个数组。


0
投票

一般来说,堆栈对象可以受益于 std::move() 的使用。正如答案指出的那样,因为 St 包含删除 St 时删除的数组,所以移动构造函数无济于事。然而,例如,包含向量的对象可以使用移动赋值(或构造,当然这比复制多个 1k 数组要好得多)。

我测试了以下示例:

struct St
{
    int32_t arr[1024];
};

struct StVect
{
    StVect(const std::initializer_list<St> values)
    {
        int i = 0;
        for (const auto o : values) { st.emplace_back(o); }
    }

    std::vector<St> st;
};

void OutputAFewValues(const StVect& st_vect)
{
    std::cout << "\n";
    std::cout << st_vect.st[0].arr[0] << " ";
    std::cout << st_vect.st[0].arr[1] << " ";
    std::cout << st_vect.st[0].arr[2] << " ";
    std::cout << st_vect.st[1].arr[0] << " ";
    std::cout << st_vect.st[1].arr[1] << " ";
    std::cout << st_vect.st[1].arr[2] << " ";
    std::cout << "\n";
}

auto test_move_st() -> void
{
    StVect st_vect{{0, 1, 3, 5, 7}, {10, 11, 13, 15, 17}};

    StVect st_vect2{{0, 2, 4, 6, 8}, {10, 12, 14, 16, 18}};

    std::cout << "\n st_vect...\n";
    OutputAFewValues(st_vect);
    OutputAFewValues(st_vect2);

    // Moves the addresses of each struct St in the vector as
    // opposed to copying each St and its array data.
    st_vect2 = std::move(st_vect);

    std::cout << "\n st_vect2...\n";

    OutputAFewValues(st_vect2);
}

对于您的具体示例,如果您想从移动中受益,那么您可以将结构保留在堆栈上,但数组应该位于堆上。这样,定位可以复制地址而不是数据。

struct st_movable
{
    static constexpr size_t ArraySize = 1024;
    std::unique_ptr<int32_t[]> arr{new int32_t[ArraySize]};
};
© www.soinside.com 2019 - 2024. All rights reserved.