简体   繁体   中英

Is it safe to convert a pointer to typed/sized enum to a pointer to the underlying type?

The following code:

void f(const uint8_t* a) {}  // <- this is an external library function
enum E : uint8_t { X, Y, Z };

int main(void) {
  E e = X;
  f(&e);  // <- error here
}

Produces the following error:

/tmp/c.cc:10:3: error: no matching function for call to 'f'
  f(&e);
  ^
/tmp/c.cc:5:6: note: candidate function not viable: no known conversion from 'E *' to 'const uint8_t *' (aka 'const unsigned char *') for 1st argument
void f(const uint8_t* e) { }

Which is surprising to me because I thought the : uint8_t in the enum's definition would mean they are necessarily represented with that underlying type. I can easily get around this with a cast:

f((uint8_t*)&e);

which I don't mind too much, but given that it's an error to omit it, is this always safe or is the : uint8_t not providing the guarantees I think it is?

It is indeed safe (although I'm not a language-lawyer): What's stored in memory is a uint8_t , and that's what you'll be pointing at. However, if f() were to take a pointer to a non-const uint8_t , then it could potentially change the value to something that's not explicitly defined as one of the E enum values. (Edit:) While this is apparently allowed by the C++ standard, it is surprising to many (see discussion in the comments below on this point), and I would encourage you to ensure it does not happen.

... but as others suggest, you're not getting the error because of your notion of safety, but because implicit conversions aren't performed between pointed-to types. You can pass an E to a function taking a uint8_t , but not an E * to a function taking a uint8_t * ; that would be - according to the language committee and in my opinion as well - an overly cavalier attitude to pointer types.

Afaik this is only legal by accident:

What you are doing is performing a reinterpret_cast and I'm assuming f is dereferencing that pointer internally. This is only legal in a very restricted set of cases and while not normative, cppreference.com gives a good overview of those:

When a pointer or reference to object whose dynamic type is DynamicType is reinterpret_cast (or C-style cast) to a pointer or reference to object of a different type AliasedType, the cast always succeeds, but the resulting pointer or reference may only be used to access the object if one of the following is true:

  • AliasedType is (possibly cv-qualified) DynamicType

  • AliasedType and DynamicType are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T (since C++11)

  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType

  • AliasedType is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to obtain a usable pointer to a struct or union given a pointer to its non-static member or element.

  • AliasedType is a (possibly cv-qualified) base class of DynamicType and DynamicType is a standard-layout class that has has no non-static data members, and AliasedType is its first base class.

  • AliasedType is char, unsigned char, or std::byte: this permits examination of the object representation of any object as an array of bytes.

If AliasedType does not satisfy these requirements, accessing the object through the new pointer or reference invokes undefined behavior. This is known as the strict aliasing rule and applies to both C++ and C programming languages.

None of those cases includes casting to a pointer to an enum's underlying type!

However:
Casting to a pointer to unsigned char* and dereferencing it is always legal and on most platforms uint8_t is just a typedef for that. So it is ok in this case, but wouldn't be if the underlying type eg would be a uint16_t .

That being said, I would not be surprised to hear that most compilers would allow such usage, even if the standard does not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM