我正在寻找一个std :: function-like对象的实现小缓冲区优化.
Boost实现了boost :: function的小缓冲区,如下所示:
union function_buffer
{
mutable void* obj_ptr;
struct type_t {
const detail::sp_typeinfo* type;
bool const_qualified;
bool volatile_qualified;
} type;
mutable void (*func_ptr)();
struct bound_memfunc_ptr_t {
void (X::*memfunc_ptr)(int);
void* obj_ptr;
} bound_memfunc_ptr;
struct obj_ref_t {
mutable void* obj_ptr;
bool is_const_qualified;
bool is_volatile_qualified;
} obj_ref;
// To relax aliasing constraints
mutable char data;
};
做的事情如下:
new (reinterpret_cast<void*>(&out_buffer.data)) functor_type(*in_functor);
此外,C 11提供了std :: aligned_union和std::aligned_storage
which seems suitable for this.前者给出了一个类型:
suitable for used as uninitialized storage for any object whose
size is at mostLen
and whose alignment is a divisor ofAlign
我很想使用类似的东西:
class MyFunction {
private:
typename std::aligned_storage<something>::type buffer;
MyFunctionVtable* vtable;
public:
template<class F>
MyFunction(F f)
{
static_assert(sizeof(F) <= sizeof(buffer) &&
alignof(F) <= alignof(buffer), "Type not suitable");
new (&buffer) F(std::move(f));
vtable = ...;
}
// [...]
};
这个(或者boost实现)是否会破坏类型别名规则以及为什么?我倾向于认为存在会引发不端行为的陷阱.
作为参考,C标准中的注释给出了aligned_storage的典型实现:
template <std::size_t Len, std::size_t Alignment>
struct aligned_storage {
typedef struct {
alignas(Alignment) unsigned char __data[Len];
} type;
};
看起来类似于sens中的boost版本,它们都依赖于char来“启用”别名.
那std :: aligned_union<> ::类型怎么样?使用未明确列出的类型是否安全?
最佳答案 对于第一个近似值,当您使用一种类型写入存储位置并使用另一种类型读取时会发生别名,并且这些类型都不是窄字符类型(char,signed char,unsigned char).
如果Boost实现在任何时候用一个成员写入function_buffer然后读取另一个成员,除非其中一个成员是数据,否则它是不安全的.报告数据成员的事实//放宽别名约束可能表明Boost开发人员认为他们可以欺骗编译器不注意别名冲突.
你提出的std :: aligned_storage或std :: aligned_union的解决方案是一个很好的解决方案,只要你的vtable只通过在你的placement-new表达式中写入时使用的类型读取new(& buffer)F(std :: move(f));,所以可以编写reinterpret_cast< F *>(& buffer)并将结果表达式用作F *类型的对象,指向F类型的对象.
使用std :: aligned_union,可以放置任何具有较小尺寸和对齐要求的类型.使用static_assert进行显式化通常是个好主意:
static_assert(sizeof(F) <= sizeof(buffer));
static_assert(alignof(F) <= alignof(buffer));
// OK to proceed
new (&buffer) F(std::move(f));