How does this operator [ ] overloading works in this case? - c++

I stumbled upon this class:
class Vec3f
{
...
float x, y, z;
...
};
inline float operator[](const int index) const
{
return (&x)[index];
}
inline float& operator[](const int index)
{
return (&x)[index];
}
The class is using the [] to access to x, y, z values as in an array so that
v[0] is the value in x, v[1] is the value in y, v[2] is the value in z, but
How does the return statement working?
Is it correct to read it like: "get the value in the address specified by index starting from the address of x"?
Do (&x) must be in parenthesis, otherwise it would return the value of the address of x[index], isn't it?

Technically this is not valid code.
But what is happening:
// Declare four variables
// That are presumably placed in memory one after the other.
float x, y, z;
In the code:
return (&x)[index];
// Here we take the address of x (thus we have a pointer to float).
// The operator [] when applied to fundamental types is equivalent to
// *(pointer + index)
// So the above code is
return *(&x + index);
// This takes the address of x. Moves index floating point numbers further
// into the address space (which is illegal).
// Then returns a `lvalue referring to the object at that location`
// If this aligns with x/y/z (it is possible but not guaranteed by the standard)
// we have an `lvalue` referring to one of these objects.
Its easy to make this work and be legal:
class Vec3f
{
float data[3];
float& x;
float& y;
float& z;
public:
float& operator[](const int index) {return data[index];}
Vec3f()
: x(data[0])
, y(data[1])
, z(data[2])
{}
Vec3f(Vec3f const& copy)
: x(data[0])
, y(data[1])
, z(data[2])
{
x = copy.x;
y = copy.y;
z = copy.z;
}
Vec3f& operator=(Vec3f const& rhs)
{
x = rhs.x;
y = rhs.y;
z = rhs.z;
return *this;
}
};

Related

How virtual functions affect the way points are generated in OpenGL?

I have a project where I draw a triangle using points, it is called Sierpinski Gasket. To achieve that I am using a class Vx which holds x and y values. The problem is it throws different values if any method is virtual and I wonder why. How virtual functions affect the way the points are generated in the following OpenGL code?
My vector class has the following declaration:
class Vx {
public:
float x;
float y;
Vx();
Vx(const float& value);
Vx(const float& x, const float& y);
Vx(const Vx& v);
float getX() const;
void setX(const float& x);
float getY() const;
void setY(const float& y);
const float operator [] (int i) const;
operator const float* () const;
operator float* ();
float& operator [] (int i);
};
std::ostream& operator<<(std::ostream& os, const Vx v);
Vx operator+(const Vx& p1, const Vx& p2);
Vx operator/(const Vx& p1, const Vx& p2);
And it's implementation is:
Vectors::Vx::Vx() : x(0), y(0)
{
}
Vectors::Vx::Vx(const float& value) : x(value), y(value)
{
}
Vectors::Vx::Vx(const float& x, const float& y) : x(x), y(y)
{
}
Vectors::Vx::Vx(const Vx& v) : x(v.x), y(v.y)
{
}
float Vectors::Vx::getX() const
{
return this->x;
}
void Vectors::Vx::setX(const float& x)
{
this->x = x;
}
float Vectors::Vx::getY() const
{
return this->y;
}
void Vectors::Vx::setY(const float& y)
{
this->y = y;
}
float& Vectors::Vx::operator[](int i)
{
return *(&x + i);
}
const float Vectors::Vx::operator[](int i) const
{
return *(&x + i);
}
Vectors::Vx::operator const float* () const
{
return static_cast<const float*>(&x);
}
Vectors::Vx::operator float* ()
{
return static_cast<float*>(&x);
}
std::ostream& Vectors::operator<<(std::ostream& os, const Vx v)
{
os << "Vx: " << "x = " << v.x << ", y = " << v.y << std::endl;
return os;
}
Vx Vectors::operator+(const Vx& p1, const Vx& p2)
{
return Vx(p1.x + p2.x, p1.y + p2.y);
}
Vx Vectors::operator/(const Vx& p1, const Vx& p2)
{
return Vx(p1.x / p2.x, p1.y / p2.y);
}
My main class starts setting up OpenGL to be ready to draw, and at some point I create a buffer with the data as following:
void sierpinskiGasket() {
using namespace Vectors;
/*
Creamos la informacion para el triangulo
*/
const int numPoints = 5000;
Vx points[numPoints];
points[0] = Vx(0.0f, 0.0f);
Vx vertices[] = {
Vx(-0.50f, -0.50f),
Vx(0.50f, -0.50f),
Vx(0.0f, 0.50f)
};
for (int i = 1; i < numPoints;i++) {
int j = rand() % 3;
points[i] = (points[i - 1] + vertices[j]) / 2;
}
/*
Generamos el buffer para el triangulo
*/
unsigned int triangle_vbo;
glGenBuffers(1, &triangle_vbo);
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
The result I get is the next triangle (which is okay):
But if I change any method of my Vx class to virtual, the next thing happens:
Class with virtual methods has implicit field - pointer to VMT (virtual method table). You can check it by printing sizeof(Vx) with and without virtual methods. Internal representation of classes and structs is not specified by the C++ standards and varies between compilers, so you should not pass classes with virtual methods to functions like glBufferData.
After some research, I found a more detailed answer to this question since it explains why virtual functions affect the way class members have different memory allocations throwing different results, in addition with some documentation from the c++ reference that explains about polymorphic objects and memory allocation too.
In fact, it is undefined behaviour and the answer to the following question explains why in a short and better way.
https://stackoverflow.com/a/48300540/13716517
But also I would like to add what the c++ reference says from the next 2 links:
https://en.cppreference.com/w/cpp/language/object
https://en.cppreference.com/w/cpp/language/memory_model
Where we can find the next documentation from:
Polymorphic objects: Objects of a class type that declares or inherits
at least one virtual function are polymorphic objects. Within each
polymorphic object, the implementation stores additional information
(in every existing implementation, it is one pointer unless optimized
out), which is used by virtual function calls and by the RTTI features
(dynamic_cast and typeid) to determine, at run time, the type with
which the object was created, regardless of the expression it is used
in. For non-polymorphic objects, the interpretation of the value is
determined from the expression in which the object is used, and is
decided at compile time.
Memory location: A memory location is an object of scalar type
(arithmetic type, pointer type, enumeration type, or std::nullptr_t)
or the largest contiguous sequence of bit fields of non-zero length
Note: Various features of the language, such as references and virtual
functions, might involve additional memory locations that are not
accessible to programs but are managed by the implementation.
Taking all that in consideration, we can see where the undefined behaviour for using virtual functions comes from in more detail, understanding how they change the behaviour when generating the points.

Using overloaded operators on this instance (this) within a class method

I am writing a class for a 2D Vector and need to do a dot product.
I have overloaded the * operator to do this, but when it comes time to perform this * this it gives me an error:
class Vector2 {
private:
float x, y;
public:
Vector2(float _x, float _y) {
x = _x;
y = _y;
}
// Dot product
float operator*(const Vector2& other) {
return x * other.x + y * other.y;
}
// Magnitude (abs)
float Magnitude() {
float dot = (this) * (this); // HERE IS THE ERROR: expression must have arithmetic or unscoped enum type
return std::sqrt(dot);
}
Vector2 operator+(const Vector2& other) {
return Vector2(x + other.x, y + other.y);
}
};
The error is: expression must have arithmetic or unscoped enum type
Any help is appreciated! Thank You :)
The this keyword is a pointer, not an actual object.
The correct code is therefore (*this) * (*this).

Using member functions in operator overloading definition (error with const)

I am getting error in the operator overloading definition. The error goes away if I remove the consts in the parameters. Is there any way I can get it working without removing the consts in the parameters? Also what is happening behind them?
class Vector3D{
public:
float x, y, z;
float dot(Vector3D& v) {
return x * v.x + y * v.y + z * v.z;
}
};
inline float operator *(const Vector3D& a, const Vector3D& b) {
return a.dot(b);
}
You should qualify the member function dot as const as well, otherwise you can't call this member function on a const object:
float dot(Vector3D const& v) const { // <-- const here
You also need to accept v by const& since you are passing it a const object.
You didn't include the error, but it says something like : "cannot call non-const method on const object". Your dot does not modify members and should be declared as const also the argument is not modifed, hence should be const:
class Vector3D{
public:
float x, y, z;
// v---------------- allow to call on const objects
float dot(const Vector3D& v) const {
// ^---------------------------------- pass parameters as const ref
return x * v.x + y * v.y + z * v.z;
}
};
inline float operator *(const Vector3D& a, const Vector3D& b) {
return a.dot(b);
}
In the Vector3D::dot function, neither the object for member function call nor the argument object are modified.
To tell this to the compiler, you should add two consts to your dot definition.
class Vector3D{
public:
float x, y, z;
float dot(const /*(1)*/ Vector3D& v) const /*(2)*/ {
return x * v.x + y * v.y + z * v.z;
}
};
inline float operator *(const Vector3D& a, const Vector3D& b) {
return a.dot(b);
}
(1) : telling that the argument object is not modified
(2) : telling that the object for member function call is not modified

How to overload an operator for integer, float and double data types simultaneously in C++

I'm creating a 2D coordinate class (named “Point”) to help me learn C++. I want to be able to perform basic arithmetic operations (+, -, *, / ...) on Point class objects (e.g. Point_a + Point_b). However, I also want to be able to perform such operations between Points and other variable types (int/float/double).
This can be done using operator/function overloading. It can be seen from my code below (addition only) that I must, as far as I am aware, include two additional functions for each additional variable type, one for the “Point + int/float/double” form and one for the “int/float/double + Point” form.
#include <iostream>
using namespace std;
class Point
{
private:
double x, y;
public:
Point(double x_in, double y_in){
setX(x_in);
setY(y_in);
}
// Getters and Setters omitted for brevity
// --- Start of Addition Operator Overloads --- (Code Block A)
friend Point operator+(const Point &p1, const Point &p2); //Point + Point
friend Point operator+(const Point &p1, int val); //Point + Int
friend Point operator+(int val, const Point &p1); //Int + Point
friend Point operator+(const Point &p1, float val); //Point + Float
friend Point operator+(float val, const Point &p1); //Float + Point
friend Point operator+(const Point &p1, double val); //Point + Double
friend Point operator+(double val, const Point &p1); //Double + Point
// --- End of Addition Operator Overloads --- (Code Block A)
};
// --- Start of Addition Operator Overload Functions --- (Code Block B)
Point operator+(const Point &p1, const Point &p2){
return Point(p1.x + p2.x, p1.y + p2.y);
}
Point operator+(const Point &p1, int val){
return Point(p1.x + val, p1.y + val);
}
Point operator+(int val, const Point &p1){
return Point(p1.x + val, p1.y + val);
}
Point operator+(const Point &p1, float val){
return Point(p1.x + val, p1.y + val);
}
Point operator+(float val, const Point &p1){
return Point(p1.x + val, p1.y + val);
}
Point operator+(const Point &p1, double val){
return Point(p1.x + val, p1.y + val);
}
Point operator+(double val, const Point &p1){
return Point(p1.x + val, p1.y + val);
}
// --- End of Addition Operator Overload Functions --- (Code Block B)
int main()
{
Point point_a( 2.00, 20.00);
Point point_b( 0.50, 5.00);
Point point_c = point_a + point_b;
cout << "X = " << point_c.getX() << " and Y = " << point_c.getY() << endl;
return 0;
}
There seems to be a lot of repetition, particular between “Point + int/float/double” type functions. I was wondering if there is a way that this could be shorted a little. Say, rather than having individual versions for integer, float and double I could have one version which would handle all three. For example converting code blocks "A" and "B" into something like:
...
// --- Start of Addition Operator Overloads --- (Code Block A)
friend Point operator+(const Point &p1, const Point &p2); //Point + Point
friend Point operator+(const Point &p1, int||float||double val); //Point + Int/Float/Double
friend Point operator+(int||float||double val, const Point &p1); //Int/Float/Double + Point
// --- End of Addition Operator Overloads --- (Code Block A)
...
...
// --- Start of Addition Operator Overload Functions --- (Code Block B)
Point operator+(const Point &p1, const Point &p2){
return Point(p1.x + p2.x, p1.y + p2.y);
}
Point operator+(const Point &p1, int||float||double val){
return Point(p1.x + val, p1.y + val);
}
Point operator+(int||float||double val, const Point &p1){
return Point(p1.x + val, p1.y + val);
}
// --- End of Addition Operator Overload Functions --- (Code Block B)
...
The above code section is intended to be representational of the desired result rather than actually functional (e.g. int OR float OR double).
In short, is there any way that I can make "friend Point operator+(const Point &p1, int val);" and its corresponding function (in code block B) accept integer, float and double values, or do I need to have an individual one for each variable type?
Thank you for your time.
Other answers mention templates but in actual fact, all numeric types will automatically be promoted to doubles.
Therefore, you only need to provide operators in terms of Point and double.
#include <iostream>
using namespace std;
class Point
{
private:
double x, y;
public:
Point(double x_in, double y_in){
setX(x_in);
setY(y_in);
}
// Getters and Setters omitted for brevity
double getX() const { return x; }
double getY() const { return y; }
void setX(double v) { x = v; }
void setY(double v) { y = v; }
// unary ops
Point& operator+=(Point const& other)
{
x += other.x;
y += other.y;
return *this;
}
Point& operator+=(double v)
{
x += v;
y += v;
return *this;
}
// --- Start of Addition Operator Overloads --- (Code Block A)
friend Point operator+(Point p1, const Point &p2)
{
p1 += p2;
return p1;
}
friend Point operator+(Point p1, double val)
{
p1 += val;
return p1;
}
friend Point operator+(double val, Point p1)
{
p1 += val;
return p1;
}
// --- End of Addition Operator Overloads --- (Code Block A)
};
// --- End of Addition Operator Overload Functions --- (Code Block B)
int main()
{
Point point_a( 2.00, 20.00);
Point point_b( 0.50, 5.00);
Point point_c = point_a + point_b;
point_c += 10;
Point point_d = point_c + 10;
Point point_e = point_d + 10.1;
cout << "X = " << point_c.getX() << " and Y = " << point_c.getY() << endl;
return 0;
}
Such can be more generalized by using templates and specializing the exceptional case, instead of providing an overloaded version for each and every matching type:
class Point
{
public:
// ...
template<typename T>
friend Point operator+(const Point &p1, T p2);
};
template<typename T>
Point operator+(const Point &p1, T val){
static_assert(std::is_artithmetic<T>::value,"An arithmetic type is required");
return Point(p1.x + val, p1.y + val);
}
// Specialization for Point
template<>
Point operator+(const Point &p1, const Point& val){
return Point(p1.x + val, p1.y + val);
}
Separate solution than the template solution is to define constructors for the types you want to support, and have a single operator+ that works on Point types.
The compiler will implicitly call the proper constructor to convert the built-in type, before invoking operator+.
This simplifies the implementation, at the expense of wasted temp objects.
// Example program
#include <iostream>
#include <string>
class Point {
private:
double x, y;
public:
Point(int val) {
this->x = val;
this->y = val;
}
Point(double x, double y) {
this->x = x;
this->y = y;
}
friend Point operator+(const Point &p1, const Point &p2) {
return Point(p1.x + p2.x, p1.y + p2.y);
}
};
int main()
{
Point p(1,2);
Point q = p + 10;
Point v = 10 + p;
}
There is a stylistic argument about explicitly white-listing supported types via the constructors, and blacklisting via static_asserts.
You can absolutely do this using templates:
// inside the class
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
friend Point operator+(const Point &p1, T val) {
// function definition
}
// Don't forget to define the function for T on the left hand side
This uses std::enable_if and std::is_arithmetic, to make this function only accept types that are "arithmetic types", basically integers and floats.
Point's operator+ implementations all converts its non-Point arguments to doubles before doing a + on elements.
The float and int overloads are nearly pointless. Just write the two double overloads.
Built-in conversion from int and float will then occur in the operator+ call.
If you really need int/float overloads and you know why and have tested that the double overload doesn't work, consider fixing the broken template conversion operator which is about the only way I can see this bbeing the case.
Failing that, cast to double explicitly.
Failing that, maybe write a template + that takes any T convertible to double, then converts and calls the above + for double. Then add a paragraph of documentation why you had to do something this stupid and complex instead of just overloading + with double. Then read that paragraph and change your mind and stick with + overloaded with double.

const pointer members and operator=

class Point {
public:
Point(int x, int y) : { x = new int(x); y = new int(y) }
...
...
Point& operator=(const Point& other) {
if(this!=&other){
delete x;
delete y;
x = new int(*other.x);
y = new int(*other.y);
}
return *this;
}
private:
const int* x;
const int* y;
}
Will this implementation of operator= work even if x and y of this were already initialized? does deleting a const pointer allow us to reassign it?
That's not a const pointer, but a pointer to const. So you can modify the pointer, you can't that which it points to.
A const pointer is
int* const x;
and your code wouldn't compile then.