I am interested in trying something where I create a custom type and then access to its members using dot semantics. For example:
Class A{ //simplified, omitting constructors and other methods
private:
float numbers[3];
public:
float x(){ return numbers[0]; }
float y(){ return numbers[1]; }
float z(){ return numbers[2]; }
}
So I can do something like this:
A a;
//do stuff to populate `numbers`
float x=a.x;
But I would also like to make the elements in numbers lvalues so I can do something like this:
A a;
a.y=5; //assigns 5 to numbers[1]
How can I do this setting method?
You can return a reference to allow assignment:
float & x(){ return numbers[0]; }
^
// usage
A a;
a.x() = 42;
You should also have a const overload, to allow read-only access to a const object:
float x() const {return numbers[0];}
^^^^^
// usage
A const a = something();
float x = a.x();
First. You made functions x, y and z but assigning them to float. This wouldn't work.
Second. Change these functions to return referencies:
class A{ //simplified, omitting constructors and other methods
private:
float numbers[3];
public:
float & x(){ return numbers[0]; }
float & y(){ return numbers[1]; }
float & z(){ return numbers[2]; }
};
...
A point;
float x = point.x();
point.x() = 42.0f;
There's another way: declare referencies as a members of class and initialize them in c-tor:
class A{ //simplified, omitting constructors and other methods
private:
float numbers[3];
public:
float & x;
float & y;
float & z;
A() : x( numbers[ 0 ] ), y( numbers[ 1 ] ), z( numbers[ 2 ] ) {}
};
...
A point;
float x = point.x;
point.x = 42.0f;
P.S. Pay an attention on comment, that gave #MikeSeymour
Not unless you actually have public variables named x, y and z.
Or you can return a reference and then do a.y() = 5
Related
With this code:
#include <iostream>
using namespace std;
class point{
public:
double get_x();
void set_x(double v);
double get_y();
void set_y(double z);
private:
double x, y;
};
point operator+(point& p1, point& p2)
{
point sum = {p1.x};// + p2.x, p1.y + p2.y};
return sum;
}
int main()
{
point a = {3.5,2.5}, b = {2.5,4.5}, c;
}
I get the following compiler errors saying the private members cannot be accessed:
point.cpp(22): error C2248: 'point::x': cannot access private member declared in class 'point'
point.cpp(17): note: see declaration of 'point::x'
point.cpp(8): note: see declaration of 'point'
I am pretty new to C++ and can't seem to figure out how to resolve this issue. Any help is much appreciated.
You declared x and y as private so they can't be accessed in that function(or outside the class) unless you make that function a friend of the class, but if you want to overload the + operator, you should do it inside the class, something like this :
class point
{
public:
point(double x, double y) // CONSTRUCTOR
:x(x), y(y)
{
}
double get_x() const{
return x;
};
void set_x(double v)
{
x = v;
};
double get_y() const{
return y;
};
void set_y(double z)
{
y = z;
};
point operator+(const point &obj)
{
double newX = this->x + obj.get_x();
double newY = this->y + obj.get_y();
point sum{newX, newY};
return sum;
};
private:
double x, y;
};
Let's say I have a struct
struct Vector3 {
float x;
float y;
float z;
};
Note that sizeof(Vector3) must remain the same.
EDIT: I am interested in solutions without setters.
Not let's create an instance of that struct Vector3 pos . How can I implement my struct so I can have something like this pos.xy = 10 // updates x and y or pos.yz = 20 // updates y and z or pos.xz = 30 // updates x and z?
Here is a solution that has the desired syntax, and doesn't increase the size of the class. It is technically correct, but rather convoluted:
union Vector3 {
struct {
float x, y, z;
auto& operator=(float f) { x = f; return *this; }
operator float&() & { return x; }
operator const float&() const & { return x; }
operator float () && { return x; }
float* operator&() { return &x; }
} x;
struct {
float x, y, z;
auto& operator=(float f) { y = f; return *this; }
operator float&() & { return y; }
operator const float&() const & { return y; }
operator float () && { return y; }
float* operator&() { return &y; }
} y;
struct {
float x, y, z;
auto& operator=(float f) { z = f; return *this; }
operator float&() & { return z; }
operator const float&() const & { return z; }
operator float () && { return z; }
float* operator&() { return &z; }
} z;
struct {
float x, y, z;
auto& operator=(float f) { x = y = f; return *this; }
} xy;
struct {
float x, y, z;
auto& operator=(float f) { y = z = f; return *this; }
} yz;
struct {
float x, y, z;
auto& operator=(float f) { z = x = f; return *this; }
} zx;
};
Another which relies on owner_of implemented here: https://gist.github.com/xymopen/352cbb55ddc2a767ed7c5999cfed4d31 which probably depends on some technically implementation specific (possibly undefined) behaviour:
struct Vector3 {
float x;
float y;
float z;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::xy);
v->x = v->y = f;
return *this;
}
} xy;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::yz);
v->y = v->z = f;
return *this;
}
} yz;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::zx);
v->z = v->x = f;
return *this;
}
} zx;
[[no_unique_address]]
struct {
auto& operator=(float f) {
Vector3* v = owner_of(this, &Vector3::zx);
v->x = v->y = v->z = f;
return *this;
}
} xyz;
};
The simple way is to provide setters for the combinations you want to set:
struct Vector3 {
float x = 0;
float y = 0;
float z = 0;
void set_xy(float v) {
x = v;
y = v;
}
};
int main(){
Vector3 pos;
pos.set_xy(42);
}
And if you need sizeof(Vector3) to stay the same, thats the only way.
Just "for fun" this is how you can get pos.set_xy = 20; literally:
struct two_setter {
float& one;
float& two;
void operator=(float v){
one = v;
two = v;
}
};
struct Vector3 {
float x = 0;
float y = 0;
float z = 0;
two_setter set_xy{x,y};
};
int main(){
Vector3 pos;
pos.set_xy = 42;
}
However, it has severe downsides. First it can have almost twice the size of the original Vector3. Moreover, because the two_setter stores references, Vector3 cannot be copied. If it would store pointers, copying would be possible, but then even more code would be required to get it right.
Alternatively it is possible to provide a xy method that returns a proxy that assigns the two members. But I am not going into detail, because pos.xy() = 3; looks really odd, has no advantage to pos.xy(3) and you really should provide a setter (or just rely on the user making two assignments when they want to make two assignments ;).
TL;DR Use a method instead of trying to get a syntax that C++ does not support out of the box.
It is possible to create an empty struct inside Vector3 with an operator=() that sets the variables of the outer struct. Of course for a variable to really take no space itself, you have to use [[no_unique_address]], which is only available since C++20. But here is an example of how it might work:
struct Vector3 {
[[no_unique_address]] struct {
auto &operator=(float val) {
Vector3 *self = (Vector3 *)(this);
self->x = val;
self->y = val;
return *this;
}
} xy;
// Add similar code for xz and yz
float x;
float y;
float z;
};
See it running on godbolt.org.
Since your type is standard-layout, I think the only legal way to do this, as per the C++ standard, is with a union that contains sub-objects with custom operator= definitions.
With a union, you're allowed to view the common-initial sequence of the active member, provided all types are standard-layout types. So if we carefully craft an object that shares the same common members (e.g. 3 float objects in the same order), then we can "swizzle" between them without violating strict-aliasing.
For us to accomplish this, we will need to create a bunch of members that all have the same data in the same order, in standard-layout type.
As a simple example, lets create a basic proxy type:
template <int...Idx>
class Vector3Proxy
{
public:
// ...
template <int...UIdx,
typename = std::enable_if_t<(sizeof...(Idx)==sizeof...(UIdx))>>
auto operator=(const Vector3Proxy<UIdx...>& other) -> Vector3Proxy&
{
((m_data[Idx] = other.m_data[UIdx]),...);
return (*this);
}
auto operator=(float x) -> Vector3Proxy&
{
((m_data[Idx] = x),...);
return (*this);
}
// ...
private:
float m_data[3];
template <int...> friend class Vector3Proxy;
};
In this example, not all members of m_data are used -- but they exist so that the "common-initial sequence" requirement is satisfied, which will allow us to view it through other standard-layout types within the union.
This can be built up as much as you need; float conversion for single-component operators, support for arithmetic, etc.
With a type like this, we can now build a Vector3 objects out of these proxy types
struct Vector3
{
union {
float _storage[3]; // for easy initialization
Vector3Proxy<0> x;
Vector3Proxy<1> y;
Vector3Proxy<2> z;
Vector3Proxy<0,1> xy;
Vector3Proxy<1,2> yz;
Vector3Proxy<0,2> xz;
// ...
};
};
Then the type can easily be used to assign to multiple values at once:
Vector3 x = {1,2,3};
x.xy = 5;
Or to assign components of one part to another:
Vector3 a = {1,2,3};
Vector3 b = {4,5,6};
a.xy = b.yz; // produces {5,6,3}
Live Example
This solution also ensures that sizeof(Vector3) does not change, since all proxy objects are the same size.
Note: It's not valid in C++ to use a union with anonymous structs, though some compilers support it. So although it might be tempting to rewrite this like:
union {
struct {
float x;
float y;
float z;
}; // invalid, since this is anonymous
struct {
...
} xy;
}
This is not valid in standard C++, and would not be a portable solution.
How can I implement my struct so I can have something like this pos.xy = 10 // updates x and y or pos.yz = 20 // updates y and z or pos.xz = 30 // updates x and z?
Just add the necessary class member functions to do this:
struct Vector3 {
float x;
float y;
float z;
void update_xy(float value) { x = y = value; }
void update_yz(float value) { y = z = value; }
void update_xz(float value) { x = z = value; }
};
I have a struct Cmplx which models complex numbers.
class Cmplx{
double x;
double y;
public:
Cmplx(int X, int Y){x = X; y = Y;}
double& operator->(...){...}
}
I need to implement the operator, such that
int main(){
Cmlpx z(1,2);
z->im = 5;
z->re = 2;
}
Changes my complex number into (2,5); I know how to do it when im and re are strings, but have no idea how to do it like this.
You might abuse of operator-> that way:
struct ComplexRef
{
ComplexRef* operator->() { return this;}
double& re;
double& im;
};
class Cmplx{
double x;
double y;
public:
Cmplx(int X, int Y){x = X; y = Y;}
ComplexRef operator->(){ return {x, y}; }
};
Demo
The overload of operator -> must either return a raw pointer, or return an object (by reference or by value) for which operator -> is in turn overloaded.
I have a class MyFloat:
class MyFloat
{
public:
float value = 0.0;
}
I use it so far in this way:
MyFloat *myfloat1 = new MyFloat;
myfloat1->value = 1.0;
How can I make
myfloat1 = 2.0;
if (myfloat1 < 3.0){
...
}
You can use operator= and operator float
class MyFloat
{
public:
float value = 0.0;
MyFloat& operator=(float f)
{
value = f;
return *this;
}
operator float() const
{
return value;
}
};
int main() {
MyFloat f;
f = 10.0f;
float x;
x = f;
return 0;
}
As others have mentiond you could provide a operator=(float), however for a MyFloat it is more natural to be constructed with a float instead of first constructing it and then assigning a value:
class MyFloat {
public:
float value = 0.0;
MyFloat(float x) : value(x) {}
MyFloat() {}
};
int main() {
MyFloat x;
x = 1.0; // works because MyFloat(float) is used
MyFloat y(2.0); // this is nicer
}
PS: as pointed out by R2RT, for x = 1.0; you dont want to use the constructor, but an assignment operator (I just left the line because thats what you were asking for). However, for a newly constructed MyFloat I would always prefer to pass the value to the constructor.
Your object is of type MyFloat, not float so you can't assign a float value to it. What you can do is assign a float value to a data member of your class by overriding the class' = assignment operator:
MyFloat& operator=(float p) {
value = p;
return *this;
}
So, I have an algorithm that takes a few sensors, scales them to a temperature and puts the temps in a global data store. However, sensor class A does more calculations that Class B needs. I can't put the new calcs in the data store, and i don't want to include class A inside class B just to get one piece of data with a getter.
Class A
{
private:
float x[4];
float y[4];
public:
//Scaling functions, etc...
}
Class B
{
private:
float c[4];
public:
//Scaling functions etc...
}
What would be the best way to get x[4] passed to class B to put in c[4]? The real classes have much more going on, this is about as simple as I think I can make. x[4] has data that needs to be used in class B.
class A
{
private:
float x[4];
float y[4];
public:
float* getXs()
{
return x;
}
}
class B
{
private:
float c[4];
public:
//Scaling functions etc...
void setXs(float *x)
{
for (int i=0;i<4;i++)
c[i] = x[i];
}
}
Well, you could use friends, if you're not willing to write accessors:
http://en.wikipedia.org/wiki/Friend_class
Some would argue this breaks encapsulation, and that a getter would be the preferred approach.
Use a getter of x[4] on an instance of A when calling the constructor of B.
#include <string.h>
class A
{
private:
float x[4];
float y[4];
public:
float const *xArray() const
{
return x;
}
};
class B
{
private:
float c[4];
public:
void setCArray(float const arr[4])
{
memcpy(c, arr, 4 * sizeof(int));
}
};
int main()
{
A a;
B b;
b.setCArray(a.xArray());
}
There are number of ways. The best depends on Your criteria.
If time is not crucial for you I would be simple and use copy constructor:
Class A
{
private:
float x[4];
float y[4];
public:
const float& X(int i) { return x[i]; }
}
Class B
{
private:
float c[4];
public:
B( const A& a ) {
for( k = 0; k < 4; k++ )
c[k] = a.X(k);
}
}
If time is crucial you can consider to use pointers copy. But be Very accurate with it:
Class A
{
private:
friend B;
float x[4];
float y[4];
public:
...
}
Class B
{
private:
const float* const c;
public:
B( const A& a ):c(a.x){}
// use c like c[4], but don't change it.
}