How does the compiler choose the right function? - c++

I wrote a small 3d vector class. In particular I wrote two functions, one for rotating the vector and one intended to return a rotated copy of the vector itself. so I have the followig:
Vector Vector::Rotate(const double angle, Vector& axis) const {
Vector b=*this;
b.Rotate(angle,axis);
return (b);
}
void Vector::Rotate(const double angle, Vector & axis) {
/* let's promote this vector to a quaternion */
Quaternion V (0,*this);
/* quaternion describing the rotation */
Quaternion q (cos(angle/2),axis*sin(angle/2));
/* actual rotation */
*this = (q*V*q.conjugate()).Vec();
}
now, when I write something like this:
vector2 = vector1.Rotate(rbo::M_PUCKER,i);
I obtain the error:
no operator "=" matches these operands
operand types are: Vector = void
I expect the compiler to understand what I want: why does he choose the void version instead of the other one returning a vector? Moreover, is it a good practise writing more versions of the same functions the way I did?

The compiler picks the const or non-const overload depending solely on the object on which the member function is called. If the object (or reference) is const it will pick the const overload.
Is it good practice? No. As it seems obvious from the fact that you got yourself confused on what the compiler should do. It is usually good practice to write code that can be easily read and interpreted without confusion :)

The return type does not participate in overload resolution. In other words you can't have functions that only differ in return type.
I think your code only compiles because one of those functions is "const".

if vector1 is not const ,it will choose
void Vector::Rotate(const double angle, Vector & axis)
for member function, there is a hiden parameter this,
Vector Vector::Rotate(const double angle, Vector& axis) const
//actually is
Vector Rotate(const Vector const * this,const double angle, Vector& axis) const
void Vector::Rotate(const double angle, Vector & axis)
//actually is
void Rotate(Vector const * this,const double angle, Vector& axis)
the pointer of your object vector1 is vector *
I guess the compile will choose the best match one

Related

Overloading an operator to allow different order of parameters in C++

Let's say I'm writing a class to represent 3D vectors for a computer graphics application. I would like to be able to multiply vectors by scalars, as shown below:
Vector3D vec(1, 2, 3); // The paramaters of the constructor are the x, y and z values
Vector3D vec1 = vec * 2; // Case A: result should be (2, 4, 6)
Vector3D vec2 = 2 * vec; // Case B: result should be (2, 4, 6)
To make case A work, I could add the following function to my class:
class Vector3D
{
public:
// ...
const Vector operator*(int scalar) const;
};
To make case B work, I would have to create a function outside of my class:
const Vector operator*(int scalar, const Vector& vec);
Note that I could also make case A work by adding the following function outside of my class:
const Vector operator*(const Vector& vec, int scalar);
My question is: what is the cleanest way to do this? I could add one function inside my class and one function outside of it, or I could add two functions outside of my class. I like the idea of adding two functions outside of the class because I could keep them next to each other, which I think is clearer since they essentially do the same thing, but does adding one of the functions inside the class have any benefits that I am not seeing?
Note that the return values of my functions are const so that users can't do this (as explained by Scott Meyers in his book Effective C++):
Vector3D vecA(1, 2, 3);
Vector3D vecB(1, 2, 3);
Vector3D vecC(1, 2, 3);
vecA * vecB = vecC;
The usual approach is to provide operator *=(int) as a member function and then write free functions operator*(Vector3D, int) and operator*(int, Vector3D) that call it.
Since multiplying a vector by scalar is symmetric, in the sense that there's no clear "superior" or "subordinate" participant, I would use a pair of free-standing operator overloads:
const Vector operator*(int scalar, const Vector& vec);
const Vector operator*(const Vector& vec, int scalar);
When you have a choice between overloading a member function vs. overloading a freestanding function, the decision should be based on the participation of the object itself in the computation with respect to function's parameters.
When the object plays central role, while parameters play peripheral role, keep the function with the object by making it a member. When the object and function parameters play similar roles, i.e. when there is a degree of symmetry between participants, a freestanding function is more appropriate.
The operator* here is symmetric (as in a * b and b * a should be equivalent operations) so you should prefer having both of the functions outside of the class.
Aside from what you mentioned - that having them near each other makes it cleaner - it also allows implicit conversions for either operand. On the other hand, if you had the operator* for Case A be a member function then implicit conversions of the left hand operand to Vector3D type is not allowed, but conversions of the right hand operand to Vector3D type is allowed (unless you mark the constructors explicit, but that's besides the point).
By using twin nonmember functions we have true symmetry, in that both operands have the same ability to be converted to the Vector3D type.
By the way, the C++ Core Guidelines written by Bjarne Stroustrup and Herb Sutter also suggest doing this.

Multiplication operator overloading

I am implementing a class Vector and defined the multiplication operator as a member of the class as
Vector operator*(const float& s);
as far as I understand, this means that the left operand is a Vector and the right operand is a float. So if I try and do something like this
Vector c(1,2,3);
Vector d = c * 2.0f; // Results in d = (2,4,6)
that's ok, however, to my eyes is weird to see first the vector then the scaling factor, so in order to have float * Vector I defined a non-member function as
Vector operator*(const float& s, const Vector& v)
{
//Invalid operands to binary expression 'const Vector' and 'float'
return v * s;
}
However I received the error in the comment. I don't know what am I missing or if this is not the way I should declare the operator in order to do the scaling in float * Vector fashion.
Notice that you're passing v as const Vector &, but your member operator* is not marked as const. So it can only be called on non-const Vector objects. This is most certainly not what you want, so just mark the member as const:
Vector operator*(const float& s) const;

Const correctness and operator *

I defined a class like this:
Quaternion& conjugate(); //negates the vector component of the quaternion
Quaternion conjugate() const; //same but in without modifying the class...
Quaternion& operator = (Quaternion const& Qrhs);
Quaternion& operator *= (Quaternion const& Q);
Quaternion operator * (Quaternion const& Qrhs) const;
now I use this functions like this:
PRINTVAR(*this); //this is the first time printed (a little macro to print line and file)
Quaternion vQ(0.,vn), resQ;
resQ = vQ*(this->conjugate()); //this is the method I want to discuss...
PRINTVAR(*this); //this is the second time
resQ = *this * resQ;
and this is the output
*this: (0:0:0:0) at line: 128 in file: Quaternions.cpp
*this: (-0:-0:-0:0) at line: 131 in file: Quaternions.cpp
I thought that by calling the operator * in the line resQ = vQ*(this should be called as const)...
why if I print *this again is changed?
here is the definition of the conjugate function:
Quaternion& Quaternion::conjugate(){
/* Given: Nothing
* Task: Invert sign of the vector
* Return: the class which will be modified
*/
V3 vec;
vec = -(this->getVector());
x() = vec[0];
y() = vec[1];
z() = vec[2];
return *this;
}
Quaternion Quaternion::conjugate() const{
Quaternion result(*this);
result.conjugate();
return result;
}
If the code you showed is in a non-const method, than the this pointer is non-const, and the non-const conjugate method is of course a better match than the const one. Return types aren't considered in overload decisions. If you want to insist that the const version is used, you can add constness: resQ = vQ*(static_cast<const Quaternion*>(this)->conjugate());
Probably the method in which you are using this is non-const.
The type of this a member function of a type A, is A * const. For const member function it is const A * const.
Therefore, your code will work as you expect it to do if you enter your code into const method.
If you want to force calling const overload of function you will need to do a const-cast:
const_cast<const Quaternion * const>(this)->conjugate();
*this is not const (although it is used in a const method), and the c++ FAQ Lite states regarding const overloading (in the FAQ's case on a subscript operator);
When you apply the subscript operator to a MyFredList object that is
non-const, the compiler will call the non-const subscript operator.
Translated, since *this is non-const, the non-const method will be called.

using reference and pointer for operator overloading on large objects

In the book of “The C++ Programming Language”, the author gives the following example and several claims.
class Matrix {
double m[4][4];
public:
Matrix( );
friend Matrix operator+(const Matrix&, const Matrix&)
};
Matrix operator+(const Matrix& arg1, const Matrix& arg2)
{
Matrix sum;
for (int i=0; i<4; i++)
sum.m[i][j ]=arg1.m[i][j]+arg2.m[i][j];
return sum;
}
The book claims that
references allow the use of expressions involving the usual arithmetic operators for large objects without excessive copying. Pointers cannot be used because it is not possible to redefine the meaning of an operator applied to a pointer.
I do not understand what does “excessive copying” refer to in the above statement. And for the statement of “Pointers cannot be used because it is not possible to redefine the meaning of an operator applied to a pointer”, I am just totally lost. Thanks for the explanation.
If operator+ was instead declared as taking its operands by value, e.g.,
Matrix operator+(Matrix arg1, Matrix arg2)
then copies of arg1 and arg2 would have to be made to be passed into the function. A simple matrix addition, e.g.,
Matrix x, y;
x + y;
would require copies of both x and y to be made; effectively, you end up having to copy 32 doubles, which, while not extremely expensive in this case, is not particularly cheap either. When you take an argument by reference, no copy has to be made.
Note that some operator overloads must take their argument by reference. As a common example, consider the << stream insertion operator: it must take its std::istream operand by reference because it is impossible to copy the stream.
Pointers cannot be used because operators cannot be overloaded for pointers: at least one operand of each operator overload must be a class or enumeration type. Even if you could use pointers, the syntax would be very awkward; instead of using x + y as shown above, you would need to use &x + &y.
The "excessive copying" part is simple. If there were no & in the parameter list, the matrices would be copied when being passed in.
References are similar to C++ pointers in some ways, and they are almost always implemented using pointers internally. One of the differences, which the book notes, is that you can use operator overloading on references to an instance of a classs.
However, you can't do the below (or any equivalent syntax):
Matrix operator+(const Matrix *arg1, const Matrix *arg2)
So you can't write:
Matrix *a, *b, *c;
c = a + b;
The reason for not allowing user defined operators for built-in types, like pointers or ints, is of course that they already have operators defined for them by the language.

Wrong operator() overload called

I am writing a matrix class and have overloaded the function call operator twice. The core of the matrix is a 2D double array. I am using the MinGW GCC compiler called from a windows console.
The first overload is meant to return a double from the array (for viewing an element).
the second overload is meant to return a reference to a location in the array (for changing the data in that location.
double operator()(int row, int col) const ; //allows view of element
double &operator()(int row, int col); //allows assignment of element
I am writing a testing routine and have discovered that the "viewing" overload never gets called. for some reason the compiler "defaults" to calling the overload that returns a reference when the following printf() statement is used.
fprintf(outp, "%6.2f\t", testMatD(i,j));
I understand that I'm insulting the gods by writing my own matrix class without using vectors and testing with C I/O functions. I will be punished thoroughly in the afterlife, no need to do it here.
Ultimately I'd like to know what is going on here and how to fix it. I'd prefer to use the cleaner looking operator overloads rather than member functions.
Any ideas?
The matrix class: irrelevant code omitted.
class Matrix
{
public:
double getElement(int row, int col)const; //returns the element at row,col
//operator overloads
double operator()(int row, int col) const ; //allows view of element
double &operator()(int row, int col); //allows assignment of element
private:
//data members
double **array; //pointer to data array
};
double Matrix::getElement(int row, int col)const{
//transform indices into true coordinates (from sorted coordinates
//only row needs to be transformed (user can only sort by row)
row = sortedArray[row];
result = array[usrZeroRow+row][usrZeroCol+col];
return result;
}
//operator overloads
double Matrix::operator()(int row, int col) const {
//this overload is used when viewing an element
return getElement(row,col);
}
double &Matrix::operator()(int row, int col){
//this overload is used when placing an element
return array[row+usrZeroRow][col+usrZeroCol];
}
The testing program: irrelevant code omitted.
int main(void){
FILE *outp;
outp = fopen("test_output.txt", "w+");
Matrix testMatD(5,7); //construct 5x7 matrix
//some initializations omitted
fprintf(outp, "%6.2f\t", testMatD(i,j)); //calls the wrong overload
}
The const member function (the "viewing" function) will only be called if the object is const:
const Matrix testMatD(5,7);
testMatD(1, 2); // will call the const member function
The overload which is called is determined solely by the parameters (including the this parameter) and not on the return type, or what you do with the return type.
This means that if you have a non-const method which has a signature that is otherwise identical to a const method (apart from possibly the return type) then the const method will only be used when called on a const object or through a const reference or pointer. When you have a non-const object, then non-const method will always be a better match.
Typically, the only way to make the distinction on whether you actually write to the returned object is to return some sort of proxy object which has an appropriate implicit conversion for reading and an overloaded assignment operator for writing. Needless to say, this usually adds considerable complexity.
Hey thanks everybody for your help, I had read similar responses to similar questions. I guess i just had to hear it one more time worded slightly differently.
My original problem was i needed two versions of the operator overload to be implemented differently depending on how that operator was being called.
-When the user simply needed to read a value, the overload would treat the matrix as const and do bounds checking to make sure the user was not trying to read data that doesn't exist.
-When the user needed to write data, the overload would resize the matrix accordingly.
of course this makes no sense as the method being called has no knowledge of what is calling it or what the user is trying to do (unless some data is passed in).
my solution was to make the operator overload execute the same for both a read and a write. thus, if the user attempts to read a location that doesn't exist, the matrix will re-size itself and return a default value. Ultimately this may sacrifice some speed if the user makes a mistake and reads data that doesn't exist, but if the user is doing that, the speed of the program is the least of his worries. so this requires that the user be a bit more careful, and i might add a data member that is a flag that indicates whether the matrix has been resized to easily allow the user to check if things went as expected.
I'm not going to post the code (unless requested) because this was more a high-level/functional problem, and the code contains so many specifics that it might cloud the discussion.
As others have mentioned, you need a const object to get a call of a const overload.
What you are trying to do here is ensure that the reference is transformed into an rvalue for ....
fprintf(outp, "%6.2f\t", double( testMatD(i,j) ) ); // double() for temporary
However, that conversion is performed automatically (§5.2.2/7) so special consideration is unnecessary.
Also, you might as well declare the two overloads to match. Make the "viewer" return a reference too.
double const &operator()(int row, int col) const ; //allows view of element