首页 文章

对齐存储和标准布局

提问于
浏览
9

考虑以下C 11代码:

#include <type_traits>

struct bar
{
    virtual void do_bar() const {}
};

struct foo
{
    std::aligned_storage<sizeof(bar),alignof(bar)>::type m_storage;
};

由于虚函数 do_bar()bar 不是标准布局 . 但是, foo 是标准布局,因为 std::aligned_storage 提供的类型是POD类型, foo 满足标准布局类型的所有其他要求 .

当我使用带有 newm_storage 存储来构建 bar 的实例时会发生什么?例如 . ,

foo f;
::new(static_cast<void *>(&f.m_storage)) bar();

这合法吗?我可以用它来欺骗我对标准布局类型的限制吗?

3 回答

  • 1

    这是你的代码:

    struct bar {
        virtual void do_bar() const {}
    };
    
    struct foo {
        std::aligned_storage<sizeof(bar), alignof(bar)>::type m_storage;
    };
    

    这可以 . struct foo 是标准布局类型,并且在给定实例 foo myFoo 的情况下,您可以将 bar 类型的对象构造为 myFoo.m_storage .

    但是,这对编译器的POV来说完全没有意义,为什么还要烦恼呢?正如@dyp在评论中明智地说的那样,“你为什么要让foo成为标准布局?”

    你手动了一些关于工会的事情 . 嗯,那没关系 . 你可以这样写:

    union DoesntWork {
        bar b;    // compiler error in C++11 due to non-standard-layout type
        int i;
    };
    
    union DoesWork {
        foo f;    // works fine in C++11, of course
        int i;
    };
    

    但是,同样明显的是,你不能指望 this 工作:

    struct car {
        int initialsequence;
    };
    struct bar {
        int initialsequence;
        virtual void do_bar() const {}
    };
    struct foo {
        std::aligned_storage<sizeof(bar), alignof(bar)>::type m_storage;
        bar& asBar() { return *reinterpret_cast<bar*>(&m_storage); }
    };
    
    union JustSilly {
        foo f;
        car c;
    } js;
    assert(&js.c.initialsequence ==         // Fails, because no matter how many
           &js.f.asBar().initialsequence);  // casts you add, bar still has a vptr!
    

    换句话说,你可以自由地欺骗编译器(通过type-punning和reinterpret_cast),但这并不能使你的谎言成真 . ;)

    另见:XY problem.

  • 0

    在OSX的XCode C 11编译器选项中尝试过,似乎对我有用 . 当然,你可能想做“:: new(static_cast(&f.m_storage))bar();”在foo的构造函数中,在foo的析构函数中调用它的析构函数 .

  • 0

    使用对齐的数量时

    1)建议使用 declspec(align(16)) or __attribute ((aligned(16)))为类或结构指定对齐方式 . 我在使用VS2010进行优化时遇到了一些错误,当时我没有这样做 .

    2)我通常避免重载新的并使用您建议的放置操作符,例如

    #include <new> // Remember this otherwise the placement operator is not defined
    SomeClass* c = (SomeClass*) _mm_malloc(sizeof(SomeClass),16);
    new c SomeClass(); // This is perfectly legal
    // Some work
    _mm_free(c);
    

    3)一个好的经验法则是在结构或类的开头放置对齐的数量 . 这样编译器就不会在成员之间进行零填充,并对此发出警告 .

相关问题