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

alf*_*lfC 7 move heap-memory stack-memory c++11 stdarray

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

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

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

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

但是后来决定将其分配给a std::vector而不必逐个元素地复制.

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

现在必须要做

std::array<double, 20> tmp = fun();
std::vector<double> v(tmp.begin(), tmp.end());
Run Code Online (Sandbox Code Playgroud)

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

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

我知道主要的障碍可能是std::array元素在堆中,而std::vector元素在堆中.很明显,即使std::vector从堆栈中写入仍然存储器的移动构造函数也将被无可挽回地破坏.

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

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

或者如果原则上std::vector可以从一个移动构造函数?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
}
Run Code Online (Sandbox Code Playgroud)

Tar*_*ama 7

看起来你想告诉std::vector使用std::array数据作为它的底层缓冲区,至少在它需要做一些重新分配之前.

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

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

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

  • 那么你可以使用自定义分配器,比如Howard Hinnant的`short_alloc`.我看到的主要问题是`vector`管理自己的大小,你需要用一个已经填充的数组来初始化它(比如`resize`加上一个自定义的allocator ::构造,当被问到值时它不会执行任何初始化-在里面). (3认同)

Bar*_*icz 5

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

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

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

为了显示:

|                      |
|----------------------|
| stack block 1        |
|----------------------|
| your vector          |
|----------------------|
| stack block 2        |
|----------------------|
|-                    -|
Run Code Online (Sandbox Code Playgroud)

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

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

此外,向量的任何大小调整都需要导致重新分配。由于数组精确地占用了它所需的内存,因此即使添加单个元素也会触发您试图避免的复制。