是否可以将 std::array 移动到 std::vector?

Is it possible to move an std::array into a std::vector?

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

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

一个用例是,一个具有 returns 一个 std::array<double, >

的功能

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

但后来决定将其分配给 std::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::vectorstd::array的内存布局是一样的,所以不是障碍。

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

所以我猜这道题也可以解读为:

有没有办法将内存从堆栈移动到堆(不管那是什么意思),是否可以与移动构造函数结合使用?

或者如果std::vector可以在原则中有一个来自std::array?[=32的移动构造函数=]

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
}

Is there a way to move memory from the stack to the heap (whatever that means) and if that can be combined with a move constructor?

我个人喜欢 "whatever that means" 位。让我们思考一下。 从堆栈移动 某些东西突然意味着堆栈的那部分突然被标记为堆分配区域并受到定期销毁。

问题在于堆栈是连续的并且被破坏,以及弹出东西。您不能只说 "hey, leave this memory bit out" - 任何连续的堆栈分配和释放都需要跳转 "over" 那部分。

举例说明:

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

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

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

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

您似乎想告诉 std::vector 使用 std::array 数据作为其底层缓冲区,至少在它需要重新分配之前是这样。

std::vector 没有这个接口。它应该自己管理其内部缓冲区,因此以统一的方式分配、跟踪和删除内存。如果你可以提供一个缓冲区来使用,你还需要提供关于它是如何分配的信息,它是否可能在离开作用域时被销毁等等。这很容易出错而且很难看,所以不可用。

可以做的是用std::move_iterator构造std::vector,将内容移出std::array。当然,这对算术类型没有什么影响,但是对于移动成本低的逻辑大对象,它可以避免大量数据复制:

std::array<BigThing, 20> a = fun();
std::vector<BigThing> b { std::make_move_iterator(a.begin()),
                          std::make_move_iterator(a.end())) };