Is it safe to reinterpret_cast<const char*> any struct that starts with a char[size] member?

Viewed 180

Suppose I have 2 different structs:

struct A {
    char a_name[512];
    ...              // A struct data
}

struct B {
    char b_name[256];
    ...              // B struct data
}

Given std::string name, I need to check if an object (be it A or B) exists in a container. As both A and B start with a char[size] member which is the name I'm looking for to compare, I tried the following:

// T is a template parameter which is always an object of type A or B
           ↓ 
for (const T& item : container)
    if (name == reinterpret_cast<const char*>(&item))

It does work as expected. I read the object as if it was an array of chars, but as A.a_name and B.b_name are guaranteed to be null terminated, I don't read what I shouldn't... or do I? Can the code above produce undefined behaviour? Can it produce any error that I'm not taking into account? If so, how can I then approach this task?

I cannot modify A or B. I chose not to overload operator== for each class because A and B are actually many more different structs which have that structure I described above (char[size] + some more data).

1 Answers

Your code should be safe if your classes are standard layout classes.

At the end of the section [class.members], we have

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member if that member is not a bit-field. ... [Note: The object and its first subobject are pointer-interconvertible]

The section on Compound Types, [basic.compound], says

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast.

However, you can get rid of the reinterpret_cast entirely by creating a helper function to return the proper field.

const char *GetName(const A &a) {
    return a.a_name;
}

const char *GetName(const B &b) {
    return b.b_name;
}
// ...
for (const T& item : container)
    if (name == GetName(item))

There are variations on this, including GetName begin a template function that returns a specific field, with specific implementations for types that have a different field name.

Related