8

这是一个关于堆栈内存和堆内存的交互以及通过std::arraystd::vector类从堆栈到堆的特殊情况的问题。

原则std::array<T>上可以看作是指向第一个元素的指针,加上一些关于数组大小的编译时信息。是否有可能让std::vector<T>构造函数考虑到这一事实并尝试通过复制指针将 的内容移动array到just 中。vector

一个用例是,一个函数返回一个std::array<double, >

std::array<double, 20> fun(){...};

但后来决定将其分配给 astd::vector而无需逐个元素复制。

std::vector<double> v = fun(); // not working code

现在必须做

std::array<double, 20> tmp = fun();
std::vector<double> v(tmp.begin(), tmp.end());

这实际上做了一些多余的工作,如果可能的话,这些工作就没有必要了std::vector<double> v(std::move(tmp)); \\ not working code

std::vector和的内存布局std::array是一样的,所以不是和障碍。

我知道主要障碍可能是std::array元素在堆栈中,而std::vector元素在堆中。很明显,即使编写移动构造函数,std::vector堆栈中的内存仍将被不可撤销地破坏。

所以我想这个问题也可以理解为:

有没有办法将内存从堆栈移动到堆(无论这意味着什么),如果它可以与移动构造函数结合使用?

或者,如果原则上std::vector可以有一个来自 a 的移动构造函数?std::array

MWE:

#include<array>
#include<vector>

std::array<double, 20> fun(){return {};} // don't change this function

int main(){
    std::array<double, 20> arr = fun(); // ok
    std::vector<double> v(arr.begin(), arr.end()); // ok, but copies and the allocation is duplicated
    std::vector<double> v2 = fun(); // not working, but the idea is that the work is not duplicated
}
4

2 回答 2

7

似乎您想告诉std::vectorstd::array数据用作其底层缓冲区,至少在它需要进行一些重新分配之前。

std::vector没有这个接口。它应该自己管理自己的内部缓冲区,因此统一分配、跟踪和删除内存。如果您可以提供要使用的缓冲区,您还需要提供有关如何分配它的信息,如果它可能在离开作用域时被破坏等。这容易出错且丑陋,因此不可用。

可以做的是构造std::vectorwithstd::move_iterator以将内容移出std::array. 当然,这不会对算术类型产生影响,但是对于移动成本低的逻辑大对象,它可以避免大量数据复制:

std::array<BigThing, 20> a = fun();
std::vector<BigThing> b { std::make_move_iterator(a.begin()),
                          std::make_move_iterator(a.end())) };
于 2015-12-01T11:33:27.477 回答
4

有没有办法将内存从堆栈移动到堆(无论这意味着什么),如果它可以与移动构造函数结合使用?

我个人喜欢“无论那意味着什么”。让我们思考一下这个问题。某些东西从堆栈移动到堆突然意味着堆栈的那部分突然被标记为堆分配区域并受到定期销毁。

这样做的问题是堆栈是连续的,并且被从堆栈中弹出的东西破坏了。你不能只说“嘿,把这个内存位放在外面”——任何连续的堆栈分配和释放都需要“跳过”那部分。

为了显示:

|                      |
|----------------------|
| stack block 1        |
|----------------------|
| your vector          |
|----------------------|
| stack block 2        |
|----------------------|
|-                    -|

如果您想展开这两个块,您需要首先将堆栈指针减少块 2 指针的大小,然后减少向量和块 1 的大小。这确实不是可能发生的事情。

因此,这里唯一可行的解​​决方案是复制到堆内存区域中。但是,这些副本比很多人预期的要快得多。即使向量有几兆字节,我想内存控制器也可以交换一些页面,而不必物理发送与数据位相对应的电信号。

此外,任何一种向量的调整大小都需要导致重新分配。由于数组占用的内存正好与它需要的一样多,因此即使添加单个元素也会触发您试图避免的副本。

于 2015-12-01T11:29:47.077 回答