对齐简单的类,以允许在没有UB的情况下进行数组访问

问题描述 投票:1回答:1

假设我有以下简单的类:

    struct  employee{
        std::string name;
        short salary;
        std::size_t age;
        employee(std::string name, short salary, std::size_t age) : name{name}, salary{salary}, age{age}{}
    };

由于我想以类似数组的方式访问雇员数组中雇员的name成员,因此我需要将偏移量整除:

    static_assert( sizeof(employee) % sizeof(std::string) == 0, "!" );

为了确保我正在使用alignas指令:

    struct alignas(sizeof(std::string)) employee{
        std::string name;
        short salary;
        std::size_t age;
        employee(std::string name, short salary, std::size_t age) : name{name}, salary{salary}, age{age}{}
    };

似乎可以完成这项工作(现在上面的static_assert通过了。

但是,当我打开clang UB(未定义的行为消毒剂),并且尝试构造此clang类的对齐版本的数组时,它会检测到错误:

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/move.h:139:31 in 
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/new_allocator.h:153:10: runtime error: member call on misaligned address 0x0000022de1b0 for type 'employee', which requires 32 byte alignment
0x0000022de1b0: note: pointer points here
 00 00 00 00  c0 e1 2d 02 00 00 00 00  05 00 00 00 00 00 00 00  44 61 76 69 64 00 00 00  00 00 00 00

那么,允许雇员的alignmentname成员兼容的正确方法是什么?(因此可以通过指针std::string*算术访问数组的成员)

奖励问题:如何对齐所有成员以允许对employee s数组的所有成员进行数组访问。

有关更多详细信息,请参见此处:Align/offset specific members of a struct

基本上,我注意到根据clang使用的解决方案是UB,我正在寻找替代方法。


通过成员的数组访问,我的意思是能够做到这一点:

employee ARRAY[2];
std::string* p = &ARRAY[0].name;
std::string& p2 = *(p + 2); // can be +3 or any necessary integer
assert( &p2 == &ARRAY[1].name );

[请注意,我发现这行得通(在我的系统中),该作业是否可以匹配跨步并且clang不能说是UB:

    struct employee{
        std::string name;
        short salary;
        std::size_t age;
        char dummy[9];
        employee() = default;
    }

这是我到目前为止发现的唯一不会产生UB的选项。我想知道是否还有更好的方法。

最惯用的路径似乎使用alignas,但根据clang也会触发UB。

    struct employee{
        std::string name alignas(32);
        short salary;
        std::size_t age;
        employee(std::string name, short salary, std::size_t age) : name{name}, salary{salary}, age{age}{}
    };
c++11 constructor memory-alignment alignas struct-member-alignment
1个回答
0
投票

所以,这很hacky。但这有效。

struct employee{
    std::string name;
    ...
};

employee ARRAY[2];
std::string* p = &ARRAY[0].name;
std::string* p2 = (std::string*)((char*)p + i * sizeof(employee));

只要您增加sizeof(employee),此方法就起作用。您不能将其强制转换为&reference,因为在那些编译器中,编译器会选择如何实现解引用,而这是通过指针算法无法实现的。

也许还有更多选择。这是非常不安全的...

© www.soinside.com 2019 - 2024. All rights reserved.