0

Is it possible to do this?

struct compound_type {
    int member;
};

void func()
{
    compound_type foo {384};
    int bar = sole_member_type_cast<int>(foo); // doesn't compile; there's no such thing
                                               // reinterpret_cast wouldn't compile here
    std::cout << bar << std::endl; // prints 384
}

I know it's possible with pointer aliasing, but it seems like this is a strict aliasing problem. (Is it?) Unions are also used, but again you aren't supposed to do so, as a union type "can hold only one of its non-static data members at a time" (ref).

In any case, would there be alignment or offset problems with this?

holomenicus
  • 151
  • 5
  • *"but this is prohibited by strict aliasing"* - I'm starting to think the strict aliasing rule is highly misunderstood. – StoryTeller - Unslander Monica Aug 17 '20 at 19:03
  • @StoryTeller-UnslanderMonica Absolutely; that's part of why I'm asking about it. I'll reword. – holomenicus Aug 17 '20 at 19:04
  • Well, you could always add a user-defined conversion function (see e.g. https://godbolt.org/z/1YdhEv), but that's not probably what you are asking. – Bob__ Aug 17 '20 at 19:08
  • @StoryTeller-UnslanderMonica: The authors of the Standard expected that if parts of the Standard together with an implementation's documentation would describe the behavior of some construct, but some other part characterized it as Undefined, implementations would give priority to the former whenever their customers would find it useful. Because the "Strict Aliasing Rule" was expected to be viewed in this light, the authors of C89 saw no need to worry about corner cases, since implementations could and presumably would support useful corner cases with or without a mandate. – supercat Aug 24 '20 at 21:27

2 Answers2

2

A simple way to cast between a struct with one public non-static member and that member is to use a structured binding:

auto [bar] = foo;  // auto& to not copy
assert(bar == 384);

This works with non-standard-layout types too. For example, if compound_type::member was an int& or a std::vector<int>, &foo and &foo.member would no longer be pointer-interconvertible.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • Any recommended reading on structured binding? The cppreference.com [page](https://en.cppreference.com/w/cpp/language/structured_binding) is useful, but I'm missing context and motivation. – holomenicus Aug 19 '20 at 05:07
1

Yes, you can do:

int bar = *reinterpret_cast<int*>(&foo); 

This is well defined since objects of compound_type and int are pointer-inter-convertible. The address of foois the same as the address of its first data member which is an int, and so the reinterpret_cast is well defined.


As pointed out in the comments by @Remy Lebeau, the conversion could be simplified to:

int bar = reinterpret_cast<int&>(foo); 

which is much easier to read, and write.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • Could you elaborate on pointer interconvertibility? Where can I read about it in online references or the standard? Edit: thanks for the link and I found another standards reference [here](http://eel.is/c++draft/basic.compound#4). – holomenicus Aug 17 '20 at 19:05
  • To nitpick, types aren't pointer-interconvertible. *Objects* are. In this case, `foo` and its first member. – StoryTeller - Unslander Monica Aug 17 '20 at 19:05
  • @StoryTeller-UnslanderMonica Aah, right, edited. Is this ok? Not *too* sure about the terminology here. – cigien Aug 17 '20 at 19:06
  • @holomenicus Added a cppreference link. – cigien Aug 17 '20 at 19:09
  • Note the `reinterpret_cast` can be simplified using a reference instead of a pointer: `int bar = reinterpret_cast(foo);` [Demo](https://ideone.com/DVR6Zf) – Remy Lebeau Aug 17 '20 at 19:14