使用 std::aligned_union 和 std::aligned_union 的小缓冲区优化别名
Aliasing for small buffer optimization with std::aligned_union and std::aligned_union
我正在研究 std::function
类对象的小缓冲区优化实现。
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
和。前者给出类型:
suitable for used as uninitialized storage for any object whose
size is at most Len
and whose alignment is a divisor of Align
我很想使用类似的东西:
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;
};
从某种意义上说,这看起来与提升版本相似,两者都依赖 char
来“启用”别名。
std::aligned_union<>::type
呢?使用未明确列出的类型是否安全?
首先,当您使用一种类型写入存储位置并使用另一种类型读取时,会发生别名,并且这些类型都不是窄字符类型(char
, signed char
, unsigned char
).
如果 Boost 实现在任何时候用一个成员写入 function_buffer
然后读取另一个成员,除非其中一个成员是 data
,否则它是不安全的。 data
成员被注释为 // To relax aliasing constraints
的事实可能表明 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));
我正在研究 std::function
类对象的小缓冲区优化实现。
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
和
suitable for used as uninitialized storage for any object whose size is at most
Len
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;
};
从某种意义上说,这看起来与提升版本相似,两者都依赖 char
来“启用”别名。
std::aligned_union<>::type
呢?使用未明确列出的类型是否安全?
首先,当您使用一种类型写入存储位置并使用另一种类型读取时,会发生别名,并且这些类型都不是窄字符类型(char
, signed char
, unsigned char
).
如果 Boost 实现在任何时候用一个成员写入 function_buffer
然后读取另一个成员,除非其中一个成员是 data
,否则它是不安全的。 data
成员被注释为 // To relax aliasing constraints
的事实可能表明 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));