This code crashes at (*function)()
. I'm running Visual Studio 2019 and compiling C++17 for Windows 10 x86_64. I've tested it on Linux with GCC (-std=c++17) and it works fine. I'm wondering if this is a problem with Visual Studio's C++ compiler or something that I'm not seeing.
#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;
}
The error is a read access violation in the std::function internals.
When you append an element to a std::vector
and that element would make the vector's size greater than its capacity the vector has to allocate new storage and copy/move all of its elements to the new space. While you've constructed complex std::function
objects in the char
arrays held in your vector, the vector doesn't know that. The underlying bytes that make up the std::function
object will get copied to the vector's new storage, but the std::function
's copy/move constructor won't get called.
The best solution would be to just use a std::vector<std::function<void()>>
directly. If you have to use a vector of blocks of raw storage for some reason then you'll need to pre-allocate space before you start inserting elements. i.e.
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;
}
Alternatively you could use a different data structure such as std::list
that maintains stable addresses when elements are added or removed.