使用std :: aligned_union和std :: aligned_union进行小缓冲区优化的别名

时间:2016-06-28 07:57:48

标签: c++ strict-aliasing

我正在研究类似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_unionstd::aligned_storage which seems suitable for this。前者给出了一种类型:

  

适合用作任何对象的未初始化存储   大小最多为Len,其对齐方式为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;
};

看起来类似于sens中的boost版本,它们都依赖于char来“启用”别名。

std::aligned_union<>::type怎么样?使用未明确列出的类型是否安全?

1 个答案:

答案 0 :(得分:2)

对于第一个近似值,当您使用一种类型写入存储位置并使用另一种类型读取时,会出现别名,并且这两种类型都不是窄字符类型char,{ {1}},signed char)。

如果Boost实现在任何时候只有一个成员写入unsigned char然后读取另一个成员,那么Boost实现是不安全的,除非其中一个成员是function_bufferdata成员被注释data的事实可能表明Boost开发人员认为他们可以欺骗编译器而不会注意到别名冲突。

您建议的// To relax aliasing constraintsstd::aligned_storage解决方案很好,只要您的std::aligned_union仅通过在您的展示位置 - 新表达式{{中写入时使用的类型进行读取1}},所以编写vtable并将结果表达式用作指向类型为new (&buffer) F(std::move(f));的对象的reinterpret_cast<F*>(&buffer)类型的对象。

使用F*可以放置任何类型较小且对齐要求较低的类型。使用static_assert进行显式化通常是个好主意:

F