convenient Vector3f classDo the C++ standards guarantee that unused private fields will influence sizeof?Is it a missed optimization, when a compile-time known reference takes space in a non-aggregate struct?When should you use a class vs a struct in C++?How to call a parent class function from derived class function?Meaning of 'const' last in a function declaration of a class?Memory comparison, which is faster?Failed to specialize function templateHow to sort “vector” which contains class objects? And why I am wrong?Accessing a static member function from another classFailure while trying to construct a std::list with an allocatorInconsistent overload resolution for constexpr member functions across compilers
Pronounceable encrypted text
Can my imp familiar still talk when shapshifted (to a raven, if that matters)?
Why does 8 bit truecolor use only 2 bits for blue?
GFI outlets tripped after power outage
Why Is Sojdlg123aljg a Common Password?
Why are UK MPs allowed to not vote (but it counts as a no)?
How to measure the statistical "distance" between two frequency distributions?
Phrase request for "work in" in the context of gyms
Was the lunar landing site always in the same plane as the CM's orbit?
Fantasy Military Arms and Armor: the Dwarven Grand Armory
What are the solutions of this Diophantine equation?
Where on Earth is it easiest to survive in the wilderness?
Why are some hotels asking you to book through Booking.com instead of matching the price at the front desk?
Project Euler Problem 45
How does the UK House of Commons think they can prolong the deadline of Brexit?
What is the purpose of the rotating plate in front of the lock?
What's the connection between a spoon (匕) and the old age (⺹)? (Kanji: 老)
Looking for the comic book where Spider-Man was [mistakenly] addressed as Super-Man
What do English-speaking kids call ice-cream on a stick?
When should IGNORE_DUP_KEY option be used on an index?
Why did Tony's Arc Reactor do this?
Friend is very nit picky about side comments I don't intend to be taken too seriously
What are the map units that WGS84 uses?
Was Rosie the Riveter sourced from a Michelangelo painting?
convenient Vector3f class
Do the C++ standards guarantee that unused private fields will influence sizeof?Is it a missed optimization, when a compile-time known reference takes space in a non-aggregate struct?When should you use a class vs a struct in C++?How to call a parent class function from derived class function?Meaning of 'const' last in a function declaration of a class?Memory comparison, which is faster?Failed to specialize function templateHow to sort “vector” which contains class objects? And why I am wrong?Accessing a static member function from another classFailure while trying to construct a std::list with an allocatorInconsistent overload resolution for constexpr member functions across compilers
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
Sometimes there is a need to have a Vector3f
class, which has x
, y
and z
members, and can be indexed as a float[3]
array at the same time (there are several questions here at SO already about this).
Something like:
struct Vector3f
float data[3];
float &x = data[0];
float &y = data[1];
float &z = data[2];
;
With this, we can write this:
Vector3f v;
v.x = 2.0f;
v.y = 3.0f;
v.z = 4.0f;
glVertex3fv(v.data);
But this implementation is bad, because references take space in the struct
(which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
But, with [[no_unique_address]]
I had this idea:
#include <new>
template <int INDEX>
class Vector3fProperty
public:
operator float() const
return propertyValue();
float &operator=(float value)
float &v = propertyValue();
v = value;
return v;
private:
float &propertyValue()
return std::launder(reinterpret_cast<float*>(this))[INDEX];
float propertyValue() const
return std::launder(reinterpret_cast<const float*>(this))[INDEX];
;
struct Vector3f
[[no_unique_address]]
Vector3fProperty<0> x;
[[no_unique_address]]
Vector3fProperty<1> y;
[[no_unique_address]]
Vector3fProperty<2> z;
float data[3];
;
static_assert(sizeof(Vector3f)==12);
So, basically, I have properties in the struct
, which handles the access to x
, y
and z
. These properties should not take space, as they are empty, and have the attribute of [[no_unique_address]]
What do you think about this approach? Does it have UB?
Note, this question is about a class, for which all these are possible:
Vector3f v;
v.x = 1;
float tmp = v.x;
float *c = v.<something>; // there, c points to a float[3] array
c++ c++20
|
show 9 more comments
Sometimes there is a need to have a Vector3f
class, which has x
, y
and z
members, and can be indexed as a float[3]
array at the same time (there are several questions here at SO already about this).
Something like:
struct Vector3f
float data[3];
float &x = data[0];
float &y = data[1];
float &z = data[2];
;
With this, we can write this:
Vector3f v;
v.x = 2.0f;
v.y = 3.0f;
v.z = 4.0f;
glVertex3fv(v.data);
But this implementation is bad, because references take space in the struct
(which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
But, with [[no_unique_address]]
I had this idea:
#include <new>
template <int INDEX>
class Vector3fProperty
public:
operator float() const
return propertyValue();
float &operator=(float value)
float &v = propertyValue();
v = value;
return v;
private:
float &propertyValue()
return std::launder(reinterpret_cast<float*>(this))[INDEX];
float propertyValue() const
return std::launder(reinterpret_cast<const float*>(this))[INDEX];
;
struct Vector3f
[[no_unique_address]]
Vector3fProperty<0> x;
[[no_unique_address]]
Vector3fProperty<1> y;
[[no_unique_address]]
Vector3fProperty<2> z;
float data[3];
;
static_assert(sizeof(Vector3f)==12);
So, basically, I have properties in the struct
, which handles the access to x
, y
and z
. These properties should not take space, as they are empty, and have the attribute of [[no_unique_address]]
What do you think about this approach? Does it have UB?
Note, this question is about a class, for which all these are possible:
Vector3f v;
v.x = 1;
float tmp = v.x;
float *c = v.<something>; // there, c points to a float[3] array
c++ c++20
2
@JesperJuhl: and there are cases, where this matters a lot.
– geza
8 hours ago
4
why not have v.data[0], v.data[1], v.data[2] and v.x(), v.y(), v.z()?
– slepic
8 hours ago
1
@JesperJuhl Unhelpful comment is unhelpful. Just because there may be specific applications where tripling the size of your type doesn't matter doesn't mean you shouldn't care at all about the implementation of fundamental library components like this.
– Barry
8 hours ago
1
@slepic: because that's a function call.v.x()=2.0f
looks ugly.v.setX(2.0f)
is not as terse as it could be.
– geza
7 hours ago
4
In this case, @JesperJuhl, I would personally assume it matters, on the grounds that "array of threefloat
s and notdouble
s, specifically namedx
,y
, andz
" is highly suggestive of 3D graphics. It's thus likely to be used in a context where a large number ofVector3f
s must be stored (to represent an object's vertices) and performance is extremely important (because it has to compete with other 3D graphics libraries), and thus essentially storing three pointers would be highly undesirable.
– Justin Time
7 hours ago
|
show 9 more comments
Sometimes there is a need to have a Vector3f
class, which has x
, y
and z
members, and can be indexed as a float[3]
array at the same time (there are several questions here at SO already about this).
Something like:
struct Vector3f
float data[3];
float &x = data[0];
float &y = data[1];
float &z = data[2];
;
With this, we can write this:
Vector3f v;
v.x = 2.0f;
v.y = 3.0f;
v.z = 4.0f;
glVertex3fv(v.data);
But this implementation is bad, because references take space in the struct
(which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
But, with [[no_unique_address]]
I had this idea:
#include <new>
template <int INDEX>
class Vector3fProperty
public:
operator float() const
return propertyValue();
float &operator=(float value)
float &v = propertyValue();
v = value;
return v;
private:
float &propertyValue()
return std::launder(reinterpret_cast<float*>(this))[INDEX];
float propertyValue() const
return std::launder(reinterpret_cast<const float*>(this))[INDEX];
;
struct Vector3f
[[no_unique_address]]
Vector3fProperty<0> x;
[[no_unique_address]]
Vector3fProperty<1> y;
[[no_unique_address]]
Vector3fProperty<2> z;
float data[3];
;
static_assert(sizeof(Vector3f)==12);
So, basically, I have properties in the struct
, which handles the access to x
, y
and z
. These properties should not take space, as they are empty, and have the attribute of [[no_unique_address]]
What do you think about this approach? Does it have UB?
Note, this question is about a class, for which all these are possible:
Vector3f v;
v.x = 1;
float tmp = v.x;
float *c = v.<something>; // there, c points to a float[3] array
c++ c++20
Sometimes there is a need to have a Vector3f
class, which has x
, y
and z
members, and can be indexed as a float[3]
array at the same time (there are several questions here at SO already about this).
Something like:
struct Vector3f
float data[3];
float &x = data[0];
float &y = data[1];
float &z = data[2];
;
With this, we can write this:
Vector3f v;
v.x = 2.0f;
v.y = 3.0f;
v.z = 4.0f;
glVertex3fv(v.data);
But this implementation is bad, because references take space in the struct
(which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
But, with [[no_unique_address]]
I had this idea:
#include <new>
template <int INDEX>
class Vector3fProperty
public:
operator float() const
return propertyValue();
float &operator=(float value)
float &v = propertyValue();
v = value;
return v;
private:
float &propertyValue()
return std::launder(reinterpret_cast<float*>(this))[INDEX];
float propertyValue() const
return std::launder(reinterpret_cast<const float*>(this))[INDEX];
;
struct Vector3f
[[no_unique_address]]
Vector3fProperty<0> x;
[[no_unique_address]]
Vector3fProperty<1> y;
[[no_unique_address]]
Vector3fProperty<2> z;
float data[3];
;
static_assert(sizeof(Vector3f)==12);
So, basically, I have properties in the struct
, which handles the access to x
, y
and z
. These properties should not take space, as they are empty, and have the attribute of [[no_unique_address]]
What do you think about this approach? Does it have UB?
Note, this question is about a class, for which all these are possible:
Vector3f v;
v.x = 1;
float tmp = v.x;
float *c = v.<something>; // there, c points to a float[3] array
c++ c++20
c++ c++20
edited 8 hours ago
geza
asked 8 hours ago
gezageza
16.2k3 gold badges39 silver badges95 bronze badges
16.2k3 gold badges39 silver badges95 bronze badges
2
@JesperJuhl: and there are cases, where this matters a lot.
– geza
8 hours ago
4
why not have v.data[0], v.data[1], v.data[2] and v.x(), v.y(), v.z()?
– slepic
8 hours ago
1
@JesperJuhl Unhelpful comment is unhelpful. Just because there may be specific applications where tripling the size of your type doesn't matter doesn't mean you shouldn't care at all about the implementation of fundamental library components like this.
– Barry
8 hours ago
1
@slepic: because that's a function call.v.x()=2.0f
looks ugly.v.setX(2.0f)
is not as terse as it could be.
– geza
7 hours ago
4
In this case, @JesperJuhl, I would personally assume it matters, on the grounds that "array of threefloat
s and notdouble
s, specifically namedx
,y
, andz
" is highly suggestive of 3D graphics. It's thus likely to be used in a context where a large number ofVector3f
s must be stored (to represent an object's vertices) and performance is extremely important (because it has to compete with other 3D graphics libraries), and thus essentially storing three pointers would be highly undesirable.
– Justin Time
7 hours ago
|
show 9 more comments
2
@JesperJuhl: and there are cases, where this matters a lot.
– geza
8 hours ago
4
why not have v.data[0], v.data[1], v.data[2] and v.x(), v.y(), v.z()?
– slepic
8 hours ago
1
@JesperJuhl Unhelpful comment is unhelpful. Just because there may be specific applications where tripling the size of your type doesn't matter doesn't mean you shouldn't care at all about the implementation of fundamental library components like this.
– Barry
8 hours ago
1
@slepic: because that's a function call.v.x()=2.0f
looks ugly.v.setX(2.0f)
is not as terse as it could be.
– geza
7 hours ago
4
In this case, @JesperJuhl, I would personally assume it matters, on the grounds that "array of threefloat
s and notdouble
s, specifically namedx
,y
, andz
" is highly suggestive of 3D graphics. It's thus likely to be used in a context where a large number ofVector3f
s must be stored (to represent an object's vertices) and performance is extremely important (because it has to compete with other 3D graphics libraries), and thus essentially storing three pointers would be highly undesirable.
– Justin Time
7 hours ago
2
2
@JesperJuhl: and there are cases, where this matters a lot.
– geza
8 hours ago
@JesperJuhl: and there are cases, where this matters a lot.
– geza
8 hours ago
4
4
why not have v.data[0], v.data[1], v.data[2] and v.x(), v.y(), v.z()?
– slepic
8 hours ago
why not have v.data[0], v.data[1], v.data[2] and v.x(), v.y(), v.z()?
– slepic
8 hours ago
1
1
@JesperJuhl Unhelpful comment is unhelpful. Just because there may be specific applications where tripling the size of your type doesn't matter doesn't mean you shouldn't care at all about the implementation of fundamental library components like this.
– Barry
8 hours ago
@JesperJuhl Unhelpful comment is unhelpful. Just because there may be specific applications where tripling the size of your type doesn't matter doesn't mean you shouldn't care at all about the implementation of fundamental library components like this.
– Barry
8 hours ago
1
1
@slepic: because that's a function call.
v.x()=2.0f
looks ugly. v.setX(2.0f)
is not as terse as it could be.– geza
7 hours ago
@slepic: because that's a function call.
v.x()=2.0f
looks ugly. v.setX(2.0f)
is not as terse as it could be.– geza
7 hours ago
4
4
In this case, @JesperJuhl, I would personally assume it matters, on the grounds that "array of three
float
s and not double
s, specifically named x
, y
, and z
" is highly suggestive of 3D graphics. It's thus likely to be used in a context where a large number of Vector3f
s must be stored (to represent an object's vertices) and performance is extremely important (because it has to compete with other 3D graphics libraries), and thus essentially storing three pointers would be highly undesirable.– Justin Time
7 hours ago
In this case, @JesperJuhl, I would personally assume it matters, on the grounds that "array of three
float
s and not double
s, specifically named x
, y
, and z
" is highly suggestive of 3D graphics. It's thus likely to be used in a context where a large number of Vector3f
s must be stored (to represent an object's vertices) and performance is extremely important (because it has to compete with other 3D graphics libraries), and thus essentially storing three pointers would be highly undesirable.– Justin Time
7 hours ago
|
show 9 more comments
5 Answers
5
active
oldest
votes
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
This looks like a complicated issue. Standard-layout classes have to be compatible between each other. And so compilers are not allowed to eliminate any member, regardless of how they are defined. For non standard-layout? Who knows. For more info read this: Do the C++ standards guarantee that unused private fields will influence sizeof?
From my experience compilers never remove class members, even if they are "unused" (e.g. formally sizeof
does use them).
Does it have UB?
I think this is UB. First of all [[no_unique_address]]
only means that the member need not have a unique address, not that it must not have a unique address. Secondly it is not clear where your data
member starts. Again, compilers are free to use or not paddings of previous [[no_unique_address]]
class members. Meaning your accessors may access incorrect piece of memory.
Another problem is that you want to access "outer" memory from the "inner" class. AFAIK such thing is also UB in C++.
What do you think about this approach?
Assuming it is correct (which is not) I still don't like it. You want getters/setters but C++ does not support this feature. So instead of doing those weird, complicated constructs (imagine other people maintaining this code) how about simply do
struct Vector3f
float data[3];
float x()
return data[0];
void x(float value)
data[0] = value;
...
;
You say this code is ugly. Maybe it is. But it is simple, easy to read and maintain. There's no UB, it does not depend on potential hacks with unions, and does exactly what you want, except for beauty requirement. :)
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
add a comment |
If this is going to live in a header, and you have some confidence in your compiler's optimizing capabilities, you can probably stick to a plain-old operator[]()
overload and expect the compiler to be smart enough to elide the call and return the element that you want. E.g.:
class Vec3f
public:
float x;
float y;
float z;
float &operator[](int i)
if(i == 0)
return x;
if(i == 1)
return y;
if(i == 2)
return z;
;
I tossed this into Compiler Explorer (https://godbolt.org/z/0X4FPL), which showed clang optimizing the operator[]
call away at -O2
, and GCC at -O3
. Less exciting than your approach, but simple and should work under most circumstances.
1
Well, this will be optimized only wheni
is known at compile time, right? godbolt.org/z/evVGPZ
– freakish
7 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
add a comment |
GLM implements this kind of functionality using anonymous struct
s inside an anonymous union
I can't personally guarantee that this is standard-compliant, but most major compilers (MSVC, GCC, Clang) will support this idiom:
struct Vector3f
union
struct
float x, y, z;
;
struct
float data[3];
;
;
Vector3f() : Vector3f(0,0,0)
Vector3f(float x, float y, float z) : x(x), y(y), z(z)
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec.data[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec.data[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
The non-standard behavior is in the anonymous struct used to group the letters together, which GCC will issue a warning about. As far as I know, the union
itself should be valid, because the datatypes are all identical, but you should still check with your compiler documentation if you're unsure whether this is valid or not.
As an added convenience, we can also overload the brackets operator to shorten our syntax a little:
struct Vector3f
/*...*/
float& operator[](size_t index) return data[index];
float operator[](size_t index) const return data[index];
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
Just for clarity, accessing inactive members in the way I am is valid according to the C++ standard, because those members share a "common subsequence":
If two union members are standard-layout types, it's well-defined to examine their common subsequence on any compiler.
CPP Reference: Union Declaration
Because x
and data[0]
are
- Both
float
s, - Both occupy the same memory,
- Are both standard Layout types as the standard defines them,
It's perfectly valid to access one or the other regardless of which is currently active.
1
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
1
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
3
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
3
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
|
show 11 more comments
As stated, this is impossible: pointer arithmetic is defined only within an array, and there’s no way (without putting a reference in the class, which takes up space in current implementations) to have v.x
refer to an array element.
add a comment |
There's no need for reinterpret_cast
, nor unions. You can define a simple operator[]
:
struct Vector3f
float x, y, z;
float& operator[](std::size_t i)return *(&x + i);
;
If you want a strong guarantee that this will work, though, you need to make sure that your class is Standard Layout. In practice, it should work in most cases, even if it's not. For instance, don't add any virtual
functions to this class.
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
add a comment |
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/4.0/"u003ecc by-sa 4.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%2f57810925%2fconvenient-vector3f-class%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
This looks like a complicated issue. Standard-layout classes have to be compatible between each other. And so compilers are not allowed to eliminate any member, regardless of how they are defined. For non standard-layout? Who knows. For more info read this: Do the C++ standards guarantee that unused private fields will influence sizeof?
From my experience compilers never remove class members, even if they are "unused" (e.g. formally sizeof
does use them).
Does it have UB?
I think this is UB. First of all [[no_unique_address]]
only means that the member need not have a unique address, not that it must not have a unique address. Secondly it is not clear where your data
member starts. Again, compilers are free to use or not paddings of previous [[no_unique_address]]
class members. Meaning your accessors may access incorrect piece of memory.
Another problem is that you want to access "outer" memory from the "inner" class. AFAIK such thing is also UB in C++.
What do you think about this approach?
Assuming it is correct (which is not) I still don't like it. You want getters/setters but C++ does not support this feature. So instead of doing those weird, complicated constructs (imagine other people maintaining this code) how about simply do
struct Vector3f
float data[3];
float x()
return data[0];
void x(float value)
data[0] = value;
...
;
You say this code is ugly. Maybe it is. But it is simple, easy to read and maintain. There's no UB, it does not depend on potential hacks with unions, and does exactly what you want, except for beauty requirement. :)
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
add a comment |
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
This looks like a complicated issue. Standard-layout classes have to be compatible between each other. And so compilers are not allowed to eliminate any member, regardless of how they are defined. For non standard-layout? Who knows. For more info read this: Do the C++ standards guarantee that unused private fields will influence sizeof?
From my experience compilers never remove class members, even if they are "unused" (e.g. formally sizeof
does use them).
Does it have UB?
I think this is UB. First of all [[no_unique_address]]
only means that the member need not have a unique address, not that it must not have a unique address. Secondly it is not clear where your data
member starts. Again, compilers are free to use or not paddings of previous [[no_unique_address]]
class members. Meaning your accessors may access incorrect piece of memory.
Another problem is that you want to access "outer" memory from the "inner" class. AFAIK such thing is also UB in C++.
What do you think about this approach?
Assuming it is correct (which is not) I still don't like it. You want getters/setters but C++ does not support this feature. So instead of doing those weird, complicated constructs (imagine other people maintaining this code) how about simply do
struct Vector3f
float data[3];
float x()
return data[0];
void x(float value)
data[0] = value;
...
;
You say this code is ugly. Maybe it is. But it is simple, easy to read and maintain. There's no UB, it does not depend on potential hacks with unions, and does exactly what you want, except for beauty requirement. :)
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
add a comment |
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
This looks like a complicated issue. Standard-layout classes have to be compatible between each other. And so compilers are not allowed to eliminate any member, regardless of how they are defined. For non standard-layout? Who knows. For more info read this: Do the C++ standards guarantee that unused private fields will influence sizeof?
From my experience compilers never remove class members, even if they are "unused" (e.g. formally sizeof
does use them).
Does it have UB?
I think this is UB. First of all [[no_unique_address]]
only means that the member need not have a unique address, not that it must not have a unique address. Secondly it is not clear where your data
member starts. Again, compilers are free to use or not paddings of previous [[no_unique_address]]
class members. Meaning your accessors may access incorrect piece of memory.
Another problem is that you want to access "outer" memory from the "inner" class. AFAIK such thing is also UB in C++.
What do you think about this approach?
Assuming it is correct (which is not) I still don't like it. You want getters/setters but C++ does not support this feature. So instead of doing those weird, complicated constructs (imagine other people maintaining this code) how about simply do
struct Vector3f
float data[3];
float x()
return data[0];
void x(float value)
data[0] = value;
...
;
You say this code is ugly. Maybe it is. But it is simple, easy to read and maintain. There's no UB, it does not depend on potential hacks with unions, and does exactly what you want, except for beauty requirement. :)
But this implementation is bad, because references take space in the struct (which is quite unfortunate. I don't see any reason why references cannot be removed in this particular case, maybe it is missed optimization from the compiler's part).
This looks like a complicated issue. Standard-layout classes have to be compatible between each other. And so compilers are not allowed to eliminate any member, regardless of how they are defined. For non standard-layout? Who knows. For more info read this: Do the C++ standards guarantee that unused private fields will influence sizeof?
From my experience compilers never remove class members, even if they are "unused" (e.g. formally sizeof
does use them).
Does it have UB?
I think this is UB. First of all [[no_unique_address]]
only means that the member need not have a unique address, not that it must not have a unique address. Secondly it is not clear where your data
member starts. Again, compilers are free to use or not paddings of previous [[no_unique_address]]
class members. Meaning your accessors may access incorrect piece of memory.
Another problem is that you want to access "outer" memory from the "inner" class. AFAIK such thing is also UB in C++.
What do you think about this approach?
Assuming it is correct (which is not) I still don't like it. You want getters/setters but C++ does not support this feature. So instead of doing those weird, complicated constructs (imagine other people maintaining this code) how about simply do
struct Vector3f
float data[3];
float x()
return data[0];
void x(float value)
data[0] = value;
...
;
You say this code is ugly. Maybe it is. But it is simple, easy to read and maintain. There's no UB, it does not depend on potential hacks with unions, and does exactly what you want, except for beauty requirement. :)
edited 6 hours ago
answered 6 hours ago
freakishfreakish
41.6k7 gold badges102 silver badges142 bronze badges
41.6k7 gold badges102 silver badges142 bronze badges
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
add a comment |
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
You may be interested, I asked the "remove class member" part here: stackoverflow.com/questions/57811424/…
– geza
6 hours ago
add a comment |
If this is going to live in a header, and you have some confidence in your compiler's optimizing capabilities, you can probably stick to a plain-old operator[]()
overload and expect the compiler to be smart enough to elide the call and return the element that you want. E.g.:
class Vec3f
public:
float x;
float y;
float z;
float &operator[](int i)
if(i == 0)
return x;
if(i == 1)
return y;
if(i == 2)
return z;
;
I tossed this into Compiler Explorer (https://godbolt.org/z/0X4FPL), which showed clang optimizing the operator[]
call away at -O2
, and GCC at -O3
. Less exciting than your approach, but simple and should work under most circumstances.
1
Well, this will be optimized only wheni
is known at compile time, right? godbolt.org/z/evVGPZ
– freakish
7 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
add a comment |
If this is going to live in a header, and you have some confidence in your compiler's optimizing capabilities, you can probably stick to a plain-old operator[]()
overload and expect the compiler to be smart enough to elide the call and return the element that you want. E.g.:
class Vec3f
public:
float x;
float y;
float z;
float &operator[](int i)
if(i == 0)
return x;
if(i == 1)
return y;
if(i == 2)
return z;
;
I tossed this into Compiler Explorer (https://godbolt.org/z/0X4FPL), which showed clang optimizing the operator[]
call away at -O2
, and GCC at -O3
. Less exciting than your approach, but simple and should work under most circumstances.
1
Well, this will be optimized only wheni
is known at compile time, right? godbolt.org/z/evVGPZ
– freakish
7 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
add a comment |
If this is going to live in a header, and you have some confidence in your compiler's optimizing capabilities, you can probably stick to a plain-old operator[]()
overload and expect the compiler to be smart enough to elide the call and return the element that you want. E.g.:
class Vec3f
public:
float x;
float y;
float z;
float &operator[](int i)
if(i == 0)
return x;
if(i == 1)
return y;
if(i == 2)
return z;
;
I tossed this into Compiler Explorer (https://godbolt.org/z/0X4FPL), which showed clang optimizing the operator[]
call away at -O2
, and GCC at -O3
. Less exciting than your approach, but simple and should work under most circumstances.
If this is going to live in a header, and you have some confidence in your compiler's optimizing capabilities, you can probably stick to a plain-old operator[]()
overload and expect the compiler to be smart enough to elide the call and return the element that you want. E.g.:
class Vec3f
public:
float x;
float y;
float z;
float &operator[](int i)
if(i == 0)
return x;
if(i == 1)
return y;
if(i == 2)
return z;
;
I tossed this into Compiler Explorer (https://godbolt.org/z/0X4FPL), which showed clang optimizing the operator[]
call away at -O2
, and GCC at -O3
. Less exciting than your approach, but simple and should work under most circumstances.
answered 7 hours ago
youngmityoungmit
3943 silver badges11 bronze badges
3943 silver badges11 bronze badges
1
Well, this will be optimized only wheni
is known at compile time, right? godbolt.org/z/evVGPZ
– freakish
7 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
add a comment |
1
Well, this will be optimized only wheni
is known at compile time, right? godbolt.org/z/evVGPZ
– freakish
7 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
1
1
Well, this will be optimized only when
i
is known at compile time, right? godbolt.org/z/evVGPZ– freakish
7 hours ago
Well, this will be optimized only when
i
is known at compile time, right? godbolt.org/z/evVGPZ– freakish
7 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
True! What you are getting in your example is definitely less efficient than the optimal. However, in most applications with a fixed-size array, I imagine it would be uncommon for there to be situations where a specific index cannot be known at compile time. Either an index will be supplied as a literal, or there will be loops over [0,2]. This is of course highly application sensitive, so the anonymous structs/unions proposed by @Xirema is probably the best way to go.
– youngmit
6 hours ago
add a comment |
GLM implements this kind of functionality using anonymous struct
s inside an anonymous union
I can't personally guarantee that this is standard-compliant, but most major compilers (MSVC, GCC, Clang) will support this idiom:
struct Vector3f
union
struct
float x, y, z;
;
struct
float data[3];
;
;
Vector3f() : Vector3f(0,0,0)
Vector3f(float x, float y, float z) : x(x), y(y), z(z)
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec.data[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec.data[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
The non-standard behavior is in the anonymous struct used to group the letters together, which GCC will issue a warning about. As far as I know, the union
itself should be valid, because the datatypes are all identical, but you should still check with your compiler documentation if you're unsure whether this is valid or not.
As an added convenience, we can also overload the brackets operator to shorten our syntax a little:
struct Vector3f
/*...*/
float& operator[](size_t index) return data[index];
float operator[](size_t index) const return data[index];
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
Just for clarity, accessing inactive members in the way I am is valid according to the C++ standard, because those members share a "common subsequence":
If two union members are standard-layout types, it's well-defined to examine their common subsequence on any compiler.
CPP Reference: Union Declaration
Because x
and data[0]
are
- Both
float
s, - Both occupy the same memory,
- Are both standard Layout types as the standard defines them,
It's perfectly valid to access one or the other regardless of which is currently active.
1
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
1
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
3
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
3
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
|
show 11 more comments
GLM implements this kind of functionality using anonymous struct
s inside an anonymous union
I can't personally guarantee that this is standard-compliant, but most major compilers (MSVC, GCC, Clang) will support this idiom:
struct Vector3f
union
struct
float x, y, z;
;
struct
float data[3];
;
;
Vector3f() : Vector3f(0,0,0)
Vector3f(float x, float y, float z) : x(x), y(y), z(z)
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec.data[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec.data[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
The non-standard behavior is in the anonymous struct used to group the letters together, which GCC will issue a warning about. As far as I know, the union
itself should be valid, because the datatypes are all identical, but you should still check with your compiler documentation if you're unsure whether this is valid or not.
As an added convenience, we can also overload the brackets operator to shorten our syntax a little:
struct Vector3f
/*...*/
float& operator[](size_t index) return data[index];
float operator[](size_t index) const return data[index];
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
Just for clarity, accessing inactive members in the way I am is valid according to the C++ standard, because those members share a "common subsequence":
If two union members are standard-layout types, it's well-defined to examine their common subsequence on any compiler.
CPP Reference: Union Declaration
Because x
and data[0]
are
- Both
float
s, - Both occupy the same memory,
- Are both standard Layout types as the standard defines them,
It's perfectly valid to access one or the other regardless of which is currently active.
1
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
1
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
3
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
3
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
|
show 11 more comments
GLM implements this kind of functionality using anonymous struct
s inside an anonymous union
I can't personally guarantee that this is standard-compliant, but most major compilers (MSVC, GCC, Clang) will support this idiom:
struct Vector3f
union
struct
float x, y, z;
;
struct
float data[3];
;
;
Vector3f() : Vector3f(0,0,0)
Vector3f(float x, float y, float z) : x(x), y(y), z(z)
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec.data[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec.data[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
The non-standard behavior is in the anonymous struct used to group the letters together, which GCC will issue a warning about. As far as I know, the union
itself should be valid, because the datatypes are all identical, but you should still check with your compiler documentation if you're unsure whether this is valid or not.
As an added convenience, we can also overload the brackets operator to shorten our syntax a little:
struct Vector3f
/*...*/
float& operator[](size_t index) return data[index];
float operator[](size_t index) const return data[index];
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
Just for clarity, accessing inactive members in the way I am is valid according to the C++ standard, because those members share a "common subsequence":
If two union members are standard-layout types, it's well-defined to examine their common subsequence on any compiler.
CPP Reference: Union Declaration
Because x
and data[0]
are
- Both
float
s, - Both occupy the same memory,
- Are both standard Layout types as the standard defines them,
It's perfectly valid to access one or the other regardless of which is currently active.
GLM implements this kind of functionality using anonymous struct
s inside an anonymous union
I can't personally guarantee that this is standard-compliant, but most major compilers (MSVC, GCC, Clang) will support this idiom:
struct Vector3f
union
struct
float x, y, z;
;
struct
float data[3];
;
;
Vector3f() : Vector3f(0,0,0)
Vector3f(float x, float y, float z) : x(x), y(y), z(z)
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec.data[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec.data[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
The non-standard behavior is in the anonymous struct used to group the letters together, which GCC will issue a warning about. As far as I know, the union
itself should be valid, because the datatypes are all identical, but you should still check with your compiler documentation if you're unsure whether this is valid or not.
As an added convenience, we can also overload the brackets operator to shorten our syntax a little:
struct Vector3f
/*...*/
float& operator[](size_t index) return data[index];
float operator[](size_t index) const return data[index];
;
int main()
Vector3f vec;
vec.x = 14.5;
std::cout << vec[0] << std::endl; //Should print 14.5
vec.y = -22.345;
std::cout << vec[1] << std::endl; //Should print -22.345
std::cout << sizeof(vec) << std::endl; //On most platforms will print 12
Just for clarity, accessing inactive members in the way I am is valid according to the C++ standard, because those members share a "common subsequence":
If two union members are standard-layout types, it's well-defined to examine their common subsequence on any compiler.
CPP Reference: Union Declaration
Because x
and data[0]
are
- Both
float
s, - Both occupy the same memory,
- Are both standard Layout types as the standard defines them,
It's perfectly valid to access one or the other regardless of which is currently active.
edited 7 hours ago
answered 7 hours ago
XiremaXirema
16.2k3 gold badges20 silver badges54 bronze badges
16.2k3 gold badges20 silver badges54 bronze badges
1
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
1
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
3
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
3
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
|
show 11 more comments
1
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
1
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
3
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
3
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
1
1
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
This is UB. C++ (unlike C) does not allow accessing other union members once initialized with one.
– freakish
7 hours ago
1
1
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@freakish It does if (and only if) the types of the inactive union members are the same as the active members and occupy the same memory.
– Xirema
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
@Xirema it is technically UB from a standards perspective. However AFAIK all compilers have chosen to define the behavior for compatibility reasons (within reason). Unless you're thinking that "compatible types" makes this OK...
– Mgetz
7 hours ago
3
3
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
it's UB and uses two GNU extensions a) anonymous struct not allowed by ISO . b) union type punning works. Not ALL compilers support that, but GNU collections works in non-strict mode. Xinera misquotes clause, because it relates to stucts with common begin, which isn't the case here.
– Swift - Friday Pie
7 hours ago
3
3
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
common sequence doesn't apply here.. it would have applied if both members were standard-layout ctypes with equivalent declarations. Either two arrays or two structs. SO it's technically type punning, because way how array is aligned may be (platform defined) different from struct.. especially if certain pragmas were used in code.
– Swift - Friday Pie
7 hours ago
|
show 11 more comments
As stated, this is impossible: pointer arithmetic is defined only within an array, and there’s no way (without putting a reference in the class, which takes up space in current implementations) to have v.x
refer to an array element.
add a comment |
As stated, this is impossible: pointer arithmetic is defined only within an array, and there’s no way (without putting a reference in the class, which takes up space in current implementations) to have v.x
refer to an array element.
add a comment |
As stated, this is impossible: pointer arithmetic is defined only within an array, and there’s no way (without putting a reference in the class, which takes up space in current implementations) to have v.x
refer to an array element.
As stated, this is impossible: pointer arithmetic is defined only within an array, and there’s no way (without putting a reference in the class, which takes up space in current implementations) to have v.x
refer to an array element.
answered 7 hours ago
Davis HerringDavis Herring
12.2k1 gold badge8 silver badges37 bronze badges
12.2k1 gold badge8 silver badges37 bronze badges
add a comment |
add a comment |
There's no need for reinterpret_cast
, nor unions. You can define a simple operator[]
:
struct Vector3f
float x, y, z;
float& operator[](std::size_t i)return *(&x + i);
;
If you want a strong guarantee that this will work, though, you need to make sure that your class is Standard Layout. In practice, it should work in most cases, even if it's not. For instance, don't add any virtual
functions to this class.
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
add a comment |
There's no need for reinterpret_cast
, nor unions. You can define a simple operator[]
:
struct Vector3f
float x, y, z;
float& operator[](std::size_t i)return *(&x + i);
;
If you want a strong guarantee that this will work, though, you need to make sure that your class is Standard Layout. In practice, it should work in most cases, even if it's not. For instance, don't add any virtual
functions to this class.
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
add a comment |
There's no need for reinterpret_cast
, nor unions. You can define a simple operator[]
:
struct Vector3f
float x, y, z;
float& operator[](std::size_t i)return *(&x + i);
;
If you want a strong guarantee that this will work, though, you need to make sure that your class is Standard Layout. In practice, it should work in most cases, even if it's not. For instance, don't add any virtual
functions to this class.
There's no need for reinterpret_cast
, nor unions. You can define a simple operator[]
:
struct Vector3f
float x, y, z;
float& operator[](std::size_t i)return *(&x + i);
;
If you want a strong guarantee that this will work, though, you need to make sure that your class is Standard Layout. In practice, it should work in most cases, even if it's not. For instance, don't add any virtual
functions to this class.
answered 6 hours ago
Cássio RenanCássio Renan
3,87419 silver badges47 bronze badges
3,87419 silver badges47 bronze badges
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
add a comment |
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
Pointer arithmetic cannot be used to move from one member to another, only within arrays.
– Davis Herring
2 hours ago
add a comment |
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%2f57810925%2fconvenient-vector3f-class%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
2
@JesperJuhl: and there are cases, where this matters a lot.
– geza
8 hours ago
4
why not have v.data[0], v.data[1], v.data[2] and v.x(), v.y(), v.z()?
– slepic
8 hours ago
1
@JesperJuhl Unhelpful comment is unhelpful. Just because there may be specific applications where tripling the size of your type doesn't matter doesn't mean you shouldn't care at all about the implementation of fundamental library components like this.
– Barry
8 hours ago
1
@slepic: because that's a function call.
v.x()=2.0f
looks ugly.v.setX(2.0f)
is not as terse as it could be.– geza
7 hours ago
4
In this case, @JesperJuhl, I would personally assume it matters, on the grounds that "array of three
float
s and notdouble
s, specifically namedx
,y
, andz
" is highly suggestive of 3D graphics. It's thus likely to be used in a context where a large number ofVector3f
s must be stored (to represent an object's vertices) and performance is extremely important (because it has to compete with other 3D graphics libraries), and thus essentially storing three pointers would be highly undesirable.– Justin Time
7 hours ago