这是一个如此简单的模式,必须有一种"好"的方法来整理它。
我有一个函数需要生成一个包含算术数据的动态大小的字节数组。
// Given that I have a function that kinda looks like this:
void dispatch(std::vector<char> data); //Will take possesion of data.
// The behavior I want, but this breaks strict aliasing
void bad_foo(int c) {
std::vector<char> real_data(c * sizeof(float));
float* raw_data = reinterpret_cast<float*>(real_data.data());
//Fill raw_data with usefull stuff...
dispatch(std::move(real_data));
}
void correct_but_slow_foo(int c) {
std::vector<float> raw_data(c);
//Fill raw_data with usefull stuff...
std::vector<char> real_data(c * sizeof(float));
std::memcpy(real_data.data(), raw_data.data(), c * sizeof(float));
dispatch(std::move(real_data));
}
不幸的是,似乎即使是 clang 的堆 elision 也无法理清这里需要做什么:见 godbolt
在最坏的情况下,我可以dispatch()
做一个模板,但这会变得非常混乱,我很好奇是否有办法摆脱我所忽略的这个混乱。
谢谢!
编辑:我脑海中闪过一个想法(当然是在发布问题后立即......):我可以real_data
视为分配池,并在它上面就地添加新的算术数据:
void fixed_foo(int c) {
std::vector<char> real_data(c * sizeof(float));
float* raw_data = new (real_data.data()) float[c];
//Fill raw_data with usefull stuff...
dispatch(std::move(real_data));
}
这看起来很时髦,但我"认为"这可能是合法的。或?
绕过别名规则的最安全方法是使用memcpy()
但不需要在数据的整个第二个副本上执行此操作。我建议在局部float
变量上完成所有float
工作,然后将其memcpy()
到real_data
缓冲区中的适当位置,一次缓冲一个元素。根据我的经验,大多数编译器都会有效地优化它。
void better_foo(int c) {
std::vector<char> real_data(c * sizeof(float));
//Fill raw_data with usefull stuff...
for (int i = 0; i < c; ++i) {
float x = my_complicated_calculation(i);
memcpy(&real_data[i * sizeof(float)], &x, sizeof(x));
}
dispatch(std::move(real_data));
}