【问题标题】:Aliasing for small buffer optimization with std::aligned_union and std::aligned_union使用 std::aligned_union 和 std::aligned_union 进行小缓冲区优化的别名
【发布时间】:2016-06-28 07:57:47
【问题描述】:

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

适合用作任何对象的未初始化存储 size 最多为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;
};

在感觉上类似于 boost 版本,两者都依赖 char 来“启用”别名。

std::aligned_union&lt;&gt;::type 呢?使用未明确列出的类型是否安全?

【问题讨论】:

  • char*void* 可以别名为其他类型而无需调用任何类型的 UB。你正在做的有点类似于libstd++std::function 实现的SBO。所以,两者似乎都是安全的(我更愿意将void* 传递给placement new)。

标签: c++ strict-aliasing


【解决方案1】:

第一个近似值,当您使用一种类型写入存储位置并使用另一种类型读取时,会发生别名,这些类型都不是窄字符类型charsigned char , unsigned char)。

如果 Boost 实现在任何时候使用一个成员写入 function_buffer,然后读取另一个成员,则它是不安全的,除非其中一个成员是 datadata 成员被注释为 // To relax aliasing constraints 的事实可能表明 Boost 开发人员认为他们可以欺骗编译器,使其不注意到别名违规。

您提出的std::aligned_storagestd::aligned_union 解决方案是一个很好的解决方案,只要您的vtable 仅通过在您的placement-new 表达式new (&amp;buffer) F(std::move(f)); 中写入时使用的类型进行读取,因此可以编写reinterpret_cast&lt;F*&gt;(&amp;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));

【讨论】:

  • 我无法真正利用/引用 C++ 的相关部分来理解/解释在这方面什么是安全的。我倾向于认为 boost 版本和 alingned_storage 一样安全。
  • @ysdx 这取决于你如何使用它。使用 boost 版本,您可能会想访问工会的非活动成员,这将是不安全的。
猜你喜欢
  • 2012-01-01
  • 2016-10-29
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
相关资源
最近更新 更多