Is interpreting a pointer to first member as the class itself well defined?Pointer to class data member “::*”Check for the right typecastingarray of type voidHow to add a method to a superclass from a subclass in C++Casting a tr1::function to a void*Error while deleting a vector pointer to pointersCasting from member function pointer to another type and back, strict-aliasing issue?Why should I use a pointer rather than the object itself?Calling a method through an ill-typed pointer? Is it legal?Compilation Error on passing smart pointer to a template class member function
What do you call a painting painted on a wall?
Explaining intravenous drug abuse to a small child
Why would a military not separate its forces into different branches?
How long did it take Captain Marvel to travel to Earth?
Referring to person by surname, keep or omit "von"?
How would you say "You forget wearing what you're wearing"?
Does Thanos's ship land in the middle of the battlefield in "Avengers: Endgame"?
Can a good but unremarkable PhD student become an accomplished professor?
Is there a reason why Turkey took the Balkan territories of the Ottoman Empire, instead of Greece or another of the Balkan states?
Dual frame in Riemannian metrics.
Dimmer switch not connected to ground
Do Jedi mind tricks work on Ewoks?
Collision domain question
Given a safe domain, are subdirectories safe as well?
What is the thing used to help pouring liquids called?
How to use awk to extract data from a file based on the content of another file?
What does the coin flipping before dying mean?
Hostile Divisor Numbers
Convert Numbers To Emoji Math
What is more safe for browsing the web: PC or smartphone?
Game artist computer workstation set-up – is this overkill?
Huffman Code in C++
How to preserve a rare version of a book?
My large rocket is still flipping over
Is interpreting a pointer to first member as the class itself well defined?
Pointer to class data member “::*”Check for the right typecastingarray of type voidHow to add a method to a superclass from a subclass in C++Casting a tr1::function to a void*Error while deleting a vector pointer to pointersCasting from member function pointer to another type and back, strict-aliasing issue?Why should I use a pointer rather than the object itself?Calling a method through an ill-typed pointer? Is it legal?Compilation Error on passing smart pointer to a template class member function
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I have some code that look like this:
template<typename T>
struct memory_block
// Very not copiable, this class cannot move
memory_block(memory_block const&) = delete;
memory_block(memory_block const&&) = delete;
memory_block(memory_block&) = delete;
memory_block(memory_block&&) = delete;
memory_block& operator=(memory_block const&) = delete;
memory_block& operator=(memory_block&&) = delete;
// The only constructor construct the `data` member with args
template<typename... Args>
explicit memory_block(Args&&... args) noexcept :
datastd::forward<Args>(args)...
T data;
;
template<typename T>
struct special_block : memory_block<T>
using memory_block<T>::memory_block;
std::vector<double> special_data;
;
// There is no other inheritance. The hierarchy ends here.
Now I have to store these types into type erased storage. I chose a vector of void*
as my container. I insert pointers of the data
member into the vector:
struct NonTrivial virtual ~NonTrivial() ;
// exposed to other code
std::vector<void*> vec;
// My code use dynamic memory instead of static
// Data, but it's simpler to show it that way.
static memory_block<int> data0;
static special_block<NonTrivial> data1;
void add_stuff_into_vec()
// Add pointer to `data` member to the vector.
vec.emplace_back(&(data0->data));
vec.emplace_back(&(data1->data));
Then later in the code, I access the data:
// Yay everything is fine, I cast the void* to it original type
int* data1 = static_cast<int*>(vec[0]);
NonTrivial* data1 = static_cast<NonTrivial*>(vec[1]);
The problem is that I want to access special_data
in the non-trivial case:
// Pretty sure this cast is valid! (famous last words)
std::vector<double>* special = static_cast<special_block<NonTrivial>*>(
static_cast<memory_block<NonTrivial>*>(vec[1]) // (1)
);
So now, the question
The problem arise at line (1)
: I have a pointer to data
(of type NonTrivial
), which is a member of memory_block<NonTrivial>
. I know that the void*
will always point to the first data member of a memory_block<T>
.
So is casting a void*
to the first member of a class into the class safe? If not, is there another way to do it? If it can make things simpler, I can get rid of the inheritance.
Also, I have no problem using std::aligned_storage
in this case. If that can solve the problem, I'll use that.
I hoped standard layout would help me in this case, but my static assert seem to fail.
My static assert:
static_assert(
std::is_standard_layout<special_block<NonTrivial>>::value,
"Not standard layout don't assume anything about the layout"
);
c++ c++11 casting void-pointers
|
show 7 more comments
I have some code that look like this:
template<typename T>
struct memory_block
// Very not copiable, this class cannot move
memory_block(memory_block const&) = delete;
memory_block(memory_block const&&) = delete;
memory_block(memory_block&) = delete;
memory_block(memory_block&&) = delete;
memory_block& operator=(memory_block const&) = delete;
memory_block& operator=(memory_block&&) = delete;
// The only constructor construct the `data` member with args
template<typename... Args>
explicit memory_block(Args&&... args) noexcept :
datastd::forward<Args>(args)...
T data;
;
template<typename T>
struct special_block : memory_block<T>
using memory_block<T>::memory_block;
std::vector<double> special_data;
;
// There is no other inheritance. The hierarchy ends here.
Now I have to store these types into type erased storage. I chose a vector of void*
as my container. I insert pointers of the data
member into the vector:
struct NonTrivial virtual ~NonTrivial() ;
// exposed to other code
std::vector<void*> vec;
// My code use dynamic memory instead of static
// Data, but it's simpler to show it that way.
static memory_block<int> data0;
static special_block<NonTrivial> data1;
void add_stuff_into_vec()
// Add pointer to `data` member to the vector.
vec.emplace_back(&(data0->data));
vec.emplace_back(&(data1->data));
Then later in the code, I access the data:
// Yay everything is fine, I cast the void* to it original type
int* data1 = static_cast<int*>(vec[0]);
NonTrivial* data1 = static_cast<NonTrivial*>(vec[1]);
The problem is that I want to access special_data
in the non-trivial case:
// Pretty sure this cast is valid! (famous last words)
std::vector<double>* special = static_cast<special_block<NonTrivial>*>(
static_cast<memory_block<NonTrivial>*>(vec[1]) // (1)
);
So now, the question
The problem arise at line (1)
: I have a pointer to data
(of type NonTrivial
), which is a member of memory_block<NonTrivial>
. I know that the void*
will always point to the first data member of a memory_block<T>
.
So is casting a void*
to the first member of a class into the class safe? If not, is there another way to do it? If it can make things simpler, I can get rid of the inheritance.
Also, I have no problem using std::aligned_storage
in this case. If that can solve the problem, I'll use that.
I hoped standard layout would help me in this case, but my static assert seem to fail.
My static assert:
static_assert(
std::is_standard_layout<special_block<NonTrivial>>::value,
"Not standard layout don't assume anything about the layout"
);
c++ c++11 casting void-pointers
Well technically I believe the compiler decides where to put member variables and how to align them. I'm not sure if you could assume it is a guarantee. You could add astatic_assert
that checkssizeof(memory_block<T>) == sizeof(T)
that could give you a guarantee :-)
– Neijwiert
May 2 at 14:00
@Neijwiert yes and no. There is some guarantees the stardard make.
– Guillaume Racicot
May 2 at 14:01
3
For standard layout types the first member is the address of the class. After that we really have no other guarantees except that members are laid out in memory in the order they are declared, but there can/will be padding between them.
– NathanOliver
May 2 at 14:05
1
@FrançoisAndrieux The type itself isn't polymorphic, only a member is. Sometimes.
– Deduplicator
May 2 at 14:09
1
If you can upgrade to C++17 you could usestd::any
and the visitor pattern withstd::visit
to handle the type erasure and access for you.
– NathanOliver
May 2 at 14:14
|
show 7 more comments
I have some code that look like this:
template<typename T>
struct memory_block
// Very not copiable, this class cannot move
memory_block(memory_block const&) = delete;
memory_block(memory_block const&&) = delete;
memory_block(memory_block&) = delete;
memory_block(memory_block&&) = delete;
memory_block& operator=(memory_block const&) = delete;
memory_block& operator=(memory_block&&) = delete;
// The only constructor construct the `data` member with args
template<typename... Args>
explicit memory_block(Args&&... args) noexcept :
datastd::forward<Args>(args)...
T data;
;
template<typename T>
struct special_block : memory_block<T>
using memory_block<T>::memory_block;
std::vector<double> special_data;
;
// There is no other inheritance. The hierarchy ends here.
Now I have to store these types into type erased storage. I chose a vector of void*
as my container. I insert pointers of the data
member into the vector:
struct NonTrivial virtual ~NonTrivial() ;
// exposed to other code
std::vector<void*> vec;
// My code use dynamic memory instead of static
// Data, but it's simpler to show it that way.
static memory_block<int> data0;
static special_block<NonTrivial> data1;
void add_stuff_into_vec()
// Add pointer to `data` member to the vector.
vec.emplace_back(&(data0->data));
vec.emplace_back(&(data1->data));
Then later in the code, I access the data:
// Yay everything is fine, I cast the void* to it original type
int* data1 = static_cast<int*>(vec[0]);
NonTrivial* data1 = static_cast<NonTrivial*>(vec[1]);
The problem is that I want to access special_data
in the non-trivial case:
// Pretty sure this cast is valid! (famous last words)
std::vector<double>* special = static_cast<special_block<NonTrivial>*>(
static_cast<memory_block<NonTrivial>*>(vec[1]) // (1)
);
So now, the question
The problem arise at line (1)
: I have a pointer to data
(of type NonTrivial
), which is a member of memory_block<NonTrivial>
. I know that the void*
will always point to the first data member of a memory_block<T>
.
So is casting a void*
to the first member of a class into the class safe? If not, is there another way to do it? If it can make things simpler, I can get rid of the inheritance.
Also, I have no problem using std::aligned_storage
in this case. If that can solve the problem, I'll use that.
I hoped standard layout would help me in this case, but my static assert seem to fail.
My static assert:
static_assert(
std::is_standard_layout<special_block<NonTrivial>>::value,
"Not standard layout don't assume anything about the layout"
);
c++ c++11 casting void-pointers
I have some code that look like this:
template<typename T>
struct memory_block
// Very not copiable, this class cannot move
memory_block(memory_block const&) = delete;
memory_block(memory_block const&&) = delete;
memory_block(memory_block&) = delete;
memory_block(memory_block&&) = delete;
memory_block& operator=(memory_block const&) = delete;
memory_block& operator=(memory_block&&) = delete;
// The only constructor construct the `data` member with args
template<typename... Args>
explicit memory_block(Args&&... args) noexcept :
datastd::forward<Args>(args)...
T data;
;
template<typename T>
struct special_block : memory_block<T>
using memory_block<T>::memory_block;
std::vector<double> special_data;
;
// There is no other inheritance. The hierarchy ends here.
Now I have to store these types into type erased storage. I chose a vector of void*
as my container. I insert pointers of the data
member into the vector:
struct NonTrivial virtual ~NonTrivial() ;
// exposed to other code
std::vector<void*> vec;
// My code use dynamic memory instead of static
// Data, but it's simpler to show it that way.
static memory_block<int> data0;
static special_block<NonTrivial> data1;
void add_stuff_into_vec()
// Add pointer to `data` member to the vector.
vec.emplace_back(&(data0->data));
vec.emplace_back(&(data1->data));
Then later in the code, I access the data:
// Yay everything is fine, I cast the void* to it original type
int* data1 = static_cast<int*>(vec[0]);
NonTrivial* data1 = static_cast<NonTrivial*>(vec[1]);
The problem is that I want to access special_data
in the non-trivial case:
// Pretty sure this cast is valid! (famous last words)
std::vector<double>* special = static_cast<special_block<NonTrivial>*>(
static_cast<memory_block<NonTrivial>*>(vec[1]) // (1)
);
So now, the question
The problem arise at line (1)
: I have a pointer to data
(of type NonTrivial
), which is a member of memory_block<NonTrivial>
. I know that the void*
will always point to the first data member of a memory_block<T>
.
So is casting a void*
to the first member of a class into the class safe? If not, is there another way to do it? If it can make things simpler, I can get rid of the inheritance.
Also, I have no problem using std::aligned_storage
in this case. If that can solve the problem, I'll use that.
I hoped standard layout would help me in this case, but my static assert seem to fail.
My static assert:
static_assert(
std::is_standard_layout<special_block<NonTrivial>>::value,
"Not standard layout don't assume anything about the layout"
);
c++ c++11 casting void-pointers
c++ c++11 casting void-pointers
edited May 2 at 15:54
Guillaume Racicot
asked May 2 at 13:55
Guillaume RacicotGuillaume Racicot
16.9k53874
16.9k53874
Well technically I believe the compiler decides where to put member variables and how to align them. I'm not sure if you could assume it is a guarantee. You could add astatic_assert
that checkssizeof(memory_block<T>) == sizeof(T)
that could give you a guarantee :-)
– Neijwiert
May 2 at 14:00
@Neijwiert yes and no. There is some guarantees the stardard make.
– Guillaume Racicot
May 2 at 14:01
3
For standard layout types the first member is the address of the class. After that we really have no other guarantees except that members are laid out in memory in the order they are declared, but there can/will be padding between them.
– NathanOliver
May 2 at 14:05
1
@FrançoisAndrieux The type itself isn't polymorphic, only a member is. Sometimes.
– Deduplicator
May 2 at 14:09
1
If you can upgrade to C++17 you could usestd::any
and the visitor pattern withstd::visit
to handle the type erasure and access for you.
– NathanOliver
May 2 at 14:14
|
show 7 more comments
Well technically I believe the compiler decides where to put member variables and how to align them. I'm not sure if you could assume it is a guarantee. You could add astatic_assert
that checkssizeof(memory_block<T>) == sizeof(T)
that could give you a guarantee :-)
– Neijwiert
May 2 at 14:00
@Neijwiert yes and no. There is some guarantees the stardard make.
– Guillaume Racicot
May 2 at 14:01
3
For standard layout types the first member is the address of the class. After that we really have no other guarantees except that members are laid out in memory in the order they are declared, but there can/will be padding between them.
– NathanOliver
May 2 at 14:05
1
@FrançoisAndrieux The type itself isn't polymorphic, only a member is. Sometimes.
– Deduplicator
May 2 at 14:09
1
If you can upgrade to C++17 you could usestd::any
and the visitor pattern withstd::visit
to handle the type erasure and access for you.
– NathanOliver
May 2 at 14:14
Well technically I believe the compiler decides where to put member variables and how to align them. I'm not sure if you could assume it is a guarantee. You could add a
static_assert
that checks sizeof(memory_block<T>) == sizeof(T)
that could give you a guarantee :-)– Neijwiert
May 2 at 14:00
Well technically I believe the compiler decides where to put member variables and how to align them. I'm not sure if you could assume it is a guarantee. You could add a
static_assert
that checks sizeof(memory_block<T>) == sizeof(T)
that could give you a guarantee :-)– Neijwiert
May 2 at 14:00
@Neijwiert yes and no. There is some guarantees the stardard make.
– Guillaume Racicot
May 2 at 14:01
@Neijwiert yes and no. There is some guarantees the stardard make.
– Guillaume Racicot
May 2 at 14:01
3
3
For standard layout types the first member is the address of the class. After that we really have no other guarantees except that members are laid out in memory in the order they are declared, but there can/will be padding between them.
– NathanOliver
May 2 at 14:05
For standard layout types the first member is the address of the class. After that we really have no other guarantees except that members are laid out in memory in the order they are declared, but there can/will be padding between them.
– NathanOliver
May 2 at 14:05
1
1
@FrançoisAndrieux The type itself isn't polymorphic, only a member is. Sometimes.
– Deduplicator
May 2 at 14:09
@FrançoisAndrieux The type itself isn't polymorphic, only a member is. Sometimes.
– Deduplicator
May 2 at 14:09
1
1
If you can upgrade to C++17 you could use
std::any
and the visitor pattern with std::visit
to handle the type erasure and access for you.– NathanOliver
May 2 at 14:14
If you can upgrade to C++17 you could use
std::any
and the visitor pattern with std::visit
to handle the type erasure and access for you.– NathanOliver
May 2 at 14:14
|
show 7 more comments
1 Answer
1
active
oldest
votes
As long as memory_block<T>
is a standard-layout type [class.prop]/3, the address of a memory_block<T>
and the address of its first member data
are pointer interconvertible [basic.compound]/4.3. If this is the case, the standard guarantees that you can reinterpret_cast
to get a pointer to one from a pointer to the other. As soon as you don't have a standard-layout type, there is no such guarantee.
For your particular case, memory_block<T>
will be standard-layout as long as T
is standard-layout. Your special_block
will never be standard layout because it contains an std::vector
(as also pointed out by @NathanOliver in his comment below), which is not guaranteed to be standard layout. In your case, since you just insert a pointer to the data
member of the memory_block<T>
subobject of your special_block<T>
, you could still make that work as long as T
is standard-layout if you reinterpret_cast
your void*
back to memory_block<T>*
and then static_cast
that to special_block<T>*
(assuming that you know for sure that the dynamic type of the complete object is actually special_block<T>
). Unfortunately, as soon as NonTrivial
enters the picture, all bets are off because NonTrivial
has a virtual method and, thus, is not standard layout which also means that memory_block<NonTrivial>
will not be standard layout…
One thing you could do is, e.g., have just a buffer to provide storage for a T
in your memory_block
and then construct the actual T
inside the storage of data
via placement new. for example:
#include <utility>
#include <new>
template <typename T>
struct memory_block
alignas(T) char data[sizeof(T)];
template <typename... Args>
explicit memory_block(Args&&... args) noexcept(noexcept(new (data) T(std::forward<Args>(args)...)))
new (data) T(std::forward<Args>(args)...);
~memory_block()
std::launder(reinterpret_cast<T*>(data))->~T();
…
;
That way memory_block<T>
will always be standard-layout…
I made some tests and it seemmemory_block<T>
is okay, but notspecial_block<T>
. Am I still okay if I cast the pointer to amemory_block<T>
before?
– Guillaume Racicot
May 2 at 14:10
1
Does this mean you would have to firststatic_cast<NonTrivial*>(vec[1])
before you thenreinterpret_cast<memory_block<NonTrivial>*>
?
– Max Langhof
May 2 at 14:10
1
@GuillaumeRacicotmemory_block<T>
is only standard layout iffT
is standard layout. SinceNonTrivial
is not standard layout (it has a virtual function), casting fromNonTrivial*
tomemory_block<NonTrivial>*
is not allowed. Going frommemory_block<NonTrivial>*
tospecial_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.
– Max Langhof
May 2 at 14:11
Might also want to point out thatspecial_block
is never standard layout since it contains avector
.
– NathanOliver
May 2 at 14:15
1
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
|
show 4 more comments
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55954067%2fis-interpreting-a-pointer-to-first-member-as-the-class-itself-well-defined%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
As long as memory_block<T>
is a standard-layout type [class.prop]/3, the address of a memory_block<T>
and the address of its first member data
are pointer interconvertible [basic.compound]/4.3. If this is the case, the standard guarantees that you can reinterpret_cast
to get a pointer to one from a pointer to the other. As soon as you don't have a standard-layout type, there is no such guarantee.
For your particular case, memory_block<T>
will be standard-layout as long as T
is standard-layout. Your special_block
will never be standard layout because it contains an std::vector
(as also pointed out by @NathanOliver in his comment below), which is not guaranteed to be standard layout. In your case, since you just insert a pointer to the data
member of the memory_block<T>
subobject of your special_block<T>
, you could still make that work as long as T
is standard-layout if you reinterpret_cast
your void*
back to memory_block<T>*
and then static_cast
that to special_block<T>*
(assuming that you know for sure that the dynamic type of the complete object is actually special_block<T>
). Unfortunately, as soon as NonTrivial
enters the picture, all bets are off because NonTrivial
has a virtual method and, thus, is not standard layout which also means that memory_block<NonTrivial>
will not be standard layout…
One thing you could do is, e.g., have just a buffer to provide storage for a T
in your memory_block
and then construct the actual T
inside the storage of data
via placement new. for example:
#include <utility>
#include <new>
template <typename T>
struct memory_block
alignas(T) char data[sizeof(T)];
template <typename... Args>
explicit memory_block(Args&&... args) noexcept(noexcept(new (data) T(std::forward<Args>(args)...)))
new (data) T(std::forward<Args>(args)...);
~memory_block()
std::launder(reinterpret_cast<T*>(data))->~T();
…
;
That way memory_block<T>
will always be standard-layout…
I made some tests and it seemmemory_block<T>
is okay, but notspecial_block<T>
. Am I still okay if I cast the pointer to amemory_block<T>
before?
– Guillaume Racicot
May 2 at 14:10
1
Does this mean you would have to firststatic_cast<NonTrivial*>(vec[1])
before you thenreinterpret_cast<memory_block<NonTrivial>*>
?
– Max Langhof
May 2 at 14:10
1
@GuillaumeRacicotmemory_block<T>
is only standard layout iffT
is standard layout. SinceNonTrivial
is not standard layout (it has a virtual function), casting fromNonTrivial*
tomemory_block<NonTrivial>*
is not allowed. Going frommemory_block<NonTrivial>*
tospecial_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.
– Max Langhof
May 2 at 14:11
Might also want to point out thatspecial_block
is never standard layout since it contains avector
.
– NathanOliver
May 2 at 14:15
1
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
|
show 4 more comments
As long as memory_block<T>
is a standard-layout type [class.prop]/3, the address of a memory_block<T>
and the address of its first member data
are pointer interconvertible [basic.compound]/4.3. If this is the case, the standard guarantees that you can reinterpret_cast
to get a pointer to one from a pointer to the other. As soon as you don't have a standard-layout type, there is no such guarantee.
For your particular case, memory_block<T>
will be standard-layout as long as T
is standard-layout. Your special_block
will never be standard layout because it contains an std::vector
(as also pointed out by @NathanOliver in his comment below), which is not guaranteed to be standard layout. In your case, since you just insert a pointer to the data
member of the memory_block<T>
subobject of your special_block<T>
, you could still make that work as long as T
is standard-layout if you reinterpret_cast
your void*
back to memory_block<T>*
and then static_cast
that to special_block<T>*
(assuming that you know for sure that the dynamic type of the complete object is actually special_block<T>
). Unfortunately, as soon as NonTrivial
enters the picture, all bets are off because NonTrivial
has a virtual method and, thus, is not standard layout which also means that memory_block<NonTrivial>
will not be standard layout…
One thing you could do is, e.g., have just a buffer to provide storage for a T
in your memory_block
and then construct the actual T
inside the storage of data
via placement new. for example:
#include <utility>
#include <new>
template <typename T>
struct memory_block
alignas(T) char data[sizeof(T)];
template <typename... Args>
explicit memory_block(Args&&... args) noexcept(noexcept(new (data) T(std::forward<Args>(args)...)))
new (data) T(std::forward<Args>(args)...);
~memory_block()
std::launder(reinterpret_cast<T*>(data))->~T();
…
;
That way memory_block<T>
will always be standard-layout…
I made some tests and it seemmemory_block<T>
is okay, but notspecial_block<T>
. Am I still okay if I cast the pointer to amemory_block<T>
before?
– Guillaume Racicot
May 2 at 14:10
1
Does this mean you would have to firststatic_cast<NonTrivial*>(vec[1])
before you thenreinterpret_cast<memory_block<NonTrivial>*>
?
– Max Langhof
May 2 at 14:10
1
@GuillaumeRacicotmemory_block<T>
is only standard layout iffT
is standard layout. SinceNonTrivial
is not standard layout (it has a virtual function), casting fromNonTrivial*
tomemory_block<NonTrivial>*
is not allowed. Going frommemory_block<NonTrivial>*
tospecial_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.
– Max Langhof
May 2 at 14:11
Might also want to point out thatspecial_block
is never standard layout since it contains avector
.
– NathanOliver
May 2 at 14:15
1
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
|
show 4 more comments
As long as memory_block<T>
is a standard-layout type [class.prop]/3, the address of a memory_block<T>
and the address of its first member data
are pointer interconvertible [basic.compound]/4.3. If this is the case, the standard guarantees that you can reinterpret_cast
to get a pointer to one from a pointer to the other. As soon as you don't have a standard-layout type, there is no such guarantee.
For your particular case, memory_block<T>
will be standard-layout as long as T
is standard-layout. Your special_block
will never be standard layout because it contains an std::vector
(as also pointed out by @NathanOliver in his comment below), which is not guaranteed to be standard layout. In your case, since you just insert a pointer to the data
member of the memory_block<T>
subobject of your special_block<T>
, you could still make that work as long as T
is standard-layout if you reinterpret_cast
your void*
back to memory_block<T>*
and then static_cast
that to special_block<T>*
(assuming that you know for sure that the dynamic type of the complete object is actually special_block<T>
). Unfortunately, as soon as NonTrivial
enters the picture, all bets are off because NonTrivial
has a virtual method and, thus, is not standard layout which also means that memory_block<NonTrivial>
will not be standard layout…
One thing you could do is, e.g., have just a buffer to provide storage for a T
in your memory_block
and then construct the actual T
inside the storage of data
via placement new. for example:
#include <utility>
#include <new>
template <typename T>
struct memory_block
alignas(T) char data[sizeof(T)];
template <typename... Args>
explicit memory_block(Args&&... args) noexcept(noexcept(new (data) T(std::forward<Args>(args)...)))
new (data) T(std::forward<Args>(args)...);
~memory_block()
std::launder(reinterpret_cast<T*>(data))->~T();
…
;
That way memory_block<T>
will always be standard-layout…
As long as memory_block<T>
is a standard-layout type [class.prop]/3, the address of a memory_block<T>
and the address of its first member data
are pointer interconvertible [basic.compound]/4.3. If this is the case, the standard guarantees that you can reinterpret_cast
to get a pointer to one from a pointer to the other. As soon as you don't have a standard-layout type, there is no such guarantee.
For your particular case, memory_block<T>
will be standard-layout as long as T
is standard-layout. Your special_block
will never be standard layout because it contains an std::vector
(as also pointed out by @NathanOliver in his comment below), which is not guaranteed to be standard layout. In your case, since you just insert a pointer to the data
member of the memory_block<T>
subobject of your special_block<T>
, you could still make that work as long as T
is standard-layout if you reinterpret_cast
your void*
back to memory_block<T>*
and then static_cast
that to special_block<T>*
(assuming that you know for sure that the dynamic type of the complete object is actually special_block<T>
). Unfortunately, as soon as NonTrivial
enters the picture, all bets are off because NonTrivial
has a virtual method and, thus, is not standard layout which also means that memory_block<NonTrivial>
will not be standard layout…
One thing you could do is, e.g., have just a buffer to provide storage for a T
in your memory_block
and then construct the actual T
inside the storage of data
via placement new. for example:
#include <utility>
#include <new>
template <typename T>
struct memory_block
alignas(T) char data[sizeof(T)];
template <typename... Args>
explicit memory_block(Args&&... args) noexcept(noexcept(new (data) T(std::forward<Args>(args)...)))
new (data) T(std::forward<Args>(args)...);
~memory_block()
std::launder(reinterpret_cast<T*>(data))->~T();
…
;
That way memory_block<T>
will always be standard-layout…
edited May 2 at 15:01
answered May 2 at 14:06
Michael KenzelMichael Kenzel
9,36311826
9,36311826
I made some tests and it seemmemory_block<T>
is okay, but notspecial_block<T>
. Am I still okay if I cast the pointer to amemory_block<T>
before?
– Guillaume Racicot
May 2 at 14:10
1
Does this mean you would have to firststatic_cast<NonTrivial*>(vec[1])
before you thenreinterpret_cast<memory_block<NonTrivial>*>
?
– Max Langhof
May 2 at 14:10
1
@GuillaumeRacicotmemory_block<T>
is only standard layout iffT
is standard layout. SinceNonTrivial
is not standard layout (it has a virtual function), casting fromNonTrivial*
tomemory_block<NonTrivial>*
is not allowed. Going frommemory_block<NonTrivial>*
tospecial_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.
– Max Langhof
May 2 at 14:11
Might also want to point out thatspecial_block
is never standard layout since it contains avector
.
– NathanOliver
May 2 at 14:15
1
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
|
show 4 more comments
I made some tests and it seemmemory_block<T>
is okay, but notspecial_block<T>
. Am I still okay if I cast the pointer to amemory_block<T>
before?
– Guillaume Racicot
May 2 at 14:10
1
Does this mean you would have to firststatic_cast<NonTrivial*>(vec[1])
before you thenreinterpret_cast<memory_block<NonTrivial>*>
?
– Max Langhof
May 2 at 14:10
1
@GuillaumeRacicotmemory_block<T>
is only standard layout iffT
is standard layout. SinceNonTrivial
is not standard layout (it has a virtual function), casting fromNonTrivial*
tomemory_block<NonTrivial>*
is not allowed. Going frommemory_block<NonTrivial>*
tospecial_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.
– Max Langhof
May 2 at 14:11
Might also want to point out thatspecial_block
is never standard layout since it contains avector
.
– NathanOliver
May 2 at 14:15
1
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
I made some tests and it seem
memory_block<T>
is okay, but not special_block<T>
. Am I still okay if I cast the pointer to a memory_block<T>
before?– Guillaume Racicot
May 2 at 14:10
I made some tests and it seem
memory_block<T>
is okay, but not special_block<T>
. Am I still okay if I cast the pointer to a memory_block<T>
before?– Guillaume Racicot
May 2 at 14:10
1
1
Does this mean you would have to first
static_cast<NonTrivial*>(vec[1])
before you then reinterpret_cast<memory_block<NonTrivial>*>
?– Max Langhof
May 2 at 14:10
Does this mean you would have to first
static_cast<NonTrivial*>(vec[1])
before you then reinterpret_cast<memory_block<NonTrivial>*>
?– Max Langhof
May 2 at 14:10
1
1
@GuillaumeRacicot
memory_block<T>
is only standard layout iff T
is standard layout. Since NonTrivial
is not standard layout (it has a virtual function), casting from NonTrivial*
to memory_block<NonTrivial>*
is not allowed. Going from memory_block<NonTrivial>*
to special_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.– Max Langhof
May 2 at 14:11
@GuillaumeRacicot
memory_block<T>
is only standard layout iff T
is standard layout. Since NonTrivial
is not standard layout (it has a virtual function), casting from NonTrivial*
to memory_block<NonTrivial>*
is not allowed. Going from memory_block<NonTrivial>*
to special_block<NonTrivial>*
is not relevant to the issue. Or at least I don't see how.– Max Langhof
May 2 at 14:11
Might also want to point out that
special_block
is never standard layout since it contains a vector
.– NathanOliver
May 2 at 14:15
Might also want to point out that
special_block
is never standard layout since it contains a vector
.– NathanOliver
May 2 at 14:15
1
1
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
@NathanOliver And because it has non-static members both in itself and in its base class.
– Max Langhof
May 2 at 14:15
|
show 4 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55954067%2fis-interpreting-a-pointer-to-first-member-as-the-class-itself-well-defined%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Well technically I believe the compiler decides where to put member variables and how to align them. I'm not sure if you could assume it is a guarantee. You could add a
static_assert
that checkssizeof(memory_block<T>) == sizeof(T)
that could give you a guarantee :-)– Neijwiert
May 2 at 14:00
@Neijwiert yes and no. There is some guarantees the stardard make.
– Guillaume Racicot
May 2 at 14:01
3
For standard layout types the first member is the address of the class. After that we really have no other guarantees except that members are laid out in memory in the order they are declared, but there can/will be padding between them.
– NathanOliver
May 2 at 14:05
1
@FrançoisAndrieux The type itself isn't polymorphic, only a member is. Sometimes.
– Deduplicator
May 2 at 14:09
1
If you can upgrade to C++17 you could use
std::any
and the visitor pattern withstd::visit
to handle the type erasure and access for you.– NathanOliver
May 2 at 14:14