将 placement new 与 std::function 一起使用不起作用

Using placement new with an std::function doesn't work

此代码在 (*function)() 崩溃。我是 运行 Visual Studio 2019 年,正在为 Windows 10 x86_64 编译 C++17。我已经使用 GCC (-std=c++17) 在 Linux 上对其进行了测试,并且工作正常。我想知道这是 Visual Studio 的 C++ 编译器的问题还是我没有发现的问题。

#include <vector>
#include <array>
#include <functional>
#include <iostream>

int main() {
  const size_t blockSize = sizeof(std::function<void()>);
  using block = std::array<char, blockSize>;
  std::vector<block> blocks;

  auto lambda = [](){
    std::cout << "The lambda was successfully called.\n";
  };

  blocks.emplace_back(); 
  new (&blocks[0]) std::function<void()>(lambda);

  blocks.emplace_back(); 
  new (&blocks[1]) std::function<void()>(lambda);

  std::function<void()> *function = (std::function<void()> *)blocks[0].data();

  (*function)();

  return 0;
}

该错误是 std::function 内部的读取访问冲突。

当您将一个元素附加到 std::vector 并且该元素会使向量的大小大于其容量时,向量必须分配新的存储空间并且 copy/move 它的所有元素都分配给新的 space。虽然您在向量中保存的 char 数组中构造了复杂的 std::function 对象,但向量并不知道这些。构成 std::function 对象的底层字节将被复制到向量的新存储中,但不会调用 std::function 的 copy/move 构造函数。

最好的解决方案是直接使用 std::vector<std::function<void()>>。如果您出于某种原因必须 使用原始存储块向量,那么您需要在开始插入元素之前预先分配space。即

int main() {
  const size_t blockSize = sizeof(std::function<void()>);
  using block = std::array<char, blockSize>;
  std::vector<block> blocks;

  auto lambda = [](){
    std::cout << "The lambda was successfully called.\n";
  };

  blocks.reserve(2);  // pre-allocate space for 2 blocks

  blocks.emplace_back(); 
  new (&blocks[0]) std::function<void()>(lambda);

  blocks.emplace_back(); 
  new (&blocks[1]) std::function<void()>(lambda);

  std::function<void()> *function = (std::function<void()> *)blocks[0].data();

  (*function)();

  return 0;
}

Live Demo

或者,您可以使用不同的数据结构,例如 std::list,它在添加或删除元素时保持稳定的地址。