Выравнивание простого класса, чтобы разрешить доступ к массиву без UB

Предположим, у меня есть следующий простой класс:

    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

Каким будет тогда правильный способ разрешить совместимый alignment сотрудника и nameчлены?(поэтому к члену массивов можно получить доступ по указателюstd::string* арифметика)

БОНУСНЫЙ вопрос: как можно выровнять все элементы, чтобы разрешить доступ к массиву для всех элементов массива employeeс.

Дополнительные сведения см. Здесь: Выравнивание / смещение определенных членов структуры

В основном я отмечаю, что сработавшие решения - это UB в соответствии с clang, и я ищу альтернативы.


Под доступом к массиву членов я имею в виду возможность:

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 но он также запускает UB в соответствии с clang.

    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}{}
    };

1 ответ

Итак, это очень взломано. Но это работает.

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).
Вы не можете преобразовать его в ссылку &, поскольку с ними компилятор выбирает, как реализовать разыменование, которое вы сделали невозможным с помощью арифметики указателей.

Может есть еще варианты. Это довольно небезопасно...

Другие вопросы по тегам