问题
Suppose I have the following simple class:
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}{}
};
Since I want array-like access to the name
member of employee in an array of employees, I need for example that the offsets are divisible:
static_assert( sizeof(employee) % sizeof(std::string) == 0, "!" );
In order to ensure that I am using the alignas
directive:
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}{}
};
Which seems to do the job (now the static_assert
above passes).
However when I turned on the clang
UB (undefined behavior sanitizer) and I try to construct an array of this aligned version of the class clang detects an error:
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
What would be then the correct way to allow a compatible alignment
of employee and the name
members? (so member of arrays can be accessed by pointer std::string*
arithmetic)
BONUS question: How can all the members be aligned to allow array access on all members of an array of employee
s.
For more details see here: Align/offset specific members of a struct
Basically I am noting that the solutions that worked are UB according to clang and I am looking for alternatives.
By array access of members I mean being able to this:
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 );
Note that I found this worked (in my system), does the job wrt to matching strides and clang doesn't say is UB:
struct employee{
std::string name;
short salary;
std::size_t age;
char dummy[9];
employee() = default;
}
This is the only option I found so far that doesn't produce UB. I wonder if there is a better way still.
The most idiomatic path seems to use alignas
but it also triggers UB according to 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:
So, it is very hacky. But this works.
struct employee{
std::string name;
...
};
employee ARRAY[2];
std::string* p = &ARRAY[0].name;
std::string* p2 = (std::string*)((char*)p + i * sizeof(employee));
This works, as long as you increment by sizeof(employee)
.
You can't cast it into a &reference since with those the compiler chooses how to implement the de-referencing, which you have made impossible by pointer arithmetic.
Maybe there are more options. This is quite unsafe...
来源:https://stackoverflow.com/questions/62317939/alignment-of-a-simple-class-to-allow-array-access-without-ub