Returning wrong instances from functions - c++

I'm having trouble figuring out why my BigInt instances along with the custom vector class (BigIntVector) change when returning out of += into + functions. Looking to my example in main.cpp we have 40 + (-30) which in my BigInt.cpp code means it will turn that into 40-30 (and then print negative symbol at end because 'isPositive' bool is false). Using the debugger, I know for certain that -= returns the correct value of 10 into +=. Therefore in += 'tempThis' contains the vector with 10 in it and is returned to + function. However when it returns to the + function, 'tempThis' in that scope becomes 40? Any reason why?
Thank you.
BigInt.cpp addition
// binary addition
BigInt BigInt::operator+(BigInt const& other) const {
BigInt tempThis = BigInt(*this);
tempThis += other; //tempThis becomes 40 which isn't 10 for 40-30!!!!!!!
return tempThis;
}
// compound addition-assignment operator
BigInt BigInt::operator+=(BigInt const& other) {
if (!other.isPositive) {
BigInt tempThis = BigInt(*this);
tempThis -= other; //tempThis is correctly assigned 10 for 40-30!!!!!!!!
cout << "get element at 0 +=: " << tempThis.bigIntVector->getElementAt(0) << endl;
return tempThis;
}
main.cpp
BigInt num11 = -30;
cout << "num11 (-30): " << num11 << endl;
BigInt num12 = 40;
cout << "num12 (40): " << num12 << endl;
BigInt num13 = num12 + num11;
cout << "num13 (-10): " << num13 << endl;
prints:
num11 (-30): -30
num12 (40): 40
num13 (-10): 40

Your compound addition-assignment operator += should be:
modifying its own value; and
returning a reference to itself, not a copy of its value.
The signature should be:
BigInt& BigInt::operator+=(BigInt const& other)
Within it, you don't want to use a temporary value, or possibly if you have failure conditions that might occur and want to guarantee behaviour, use a temporary variable and then overwrite your 'this' object if it succeeds.
Have a look at this page: http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html
for more information. Search within the page for "Compound Assignment Operators" if you don't want to read the whole thing.

You're missing the overload for the assignment operator and you need to return references to BigInt i.e.
BigInt& BigInt::operator+(BigInt const& other) const

Related

Immutable class with const attributes

I am trying to define an immutable object FRACTION. Because NOMINATOR and DENOMINATOR defines the object that is created, I make them const.
class Fraction{
const int nominator, denominator;
public:
Fraction(int nominator, int denominator):nominator(nominator), denominator(denominator){}
Fraction(const Fraction& copy): nominator(copy.nominator), denominator(copy.denominator){
// no need to check for a denominator of 0 here since copy must already be a valid Fraction
std::cout << "Copy constructor called\n"; // just to prove it works
}
Fraction Multiply(Fraction frac){
return Fraction(nominator * frac.nominator, denominator * frac.denominator);
}
string toString(){
return "[" + to_string(nominator) + "/" + to_string(denominator) + "]";
}
static void Test();
};
void Fraction::Test(){
cout << endl << "--- TEST: Fraction ----------------------------------------------" << endl;
Fraction fracA = Fraction(2, 1);
Fraction fracB = fracA;
cout << "Before multiplying: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << endl;
Fraction fracC = fracB.Multiply(fracB);
cout << "After multiplying: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << ", fracC=" << fracC.toString() << endl;
//--Update fracB using itself
//fracB = fracB.Krat(fracB);
//cout << "After changing fracB: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << ", fracC=" << fracC.toString() << endl;
}
Everything seems to work fine except when I try:
fracB = fracB.Multiply(fracB);
then I get an error:
error: object of type 'Fraction' cannot be assigned because its copy assignment operator is implicitly deleted
While
fracC = fracB.Multiply(fracB);
is ok.
I thought the solution would be to deliver custom copy assignment operator, but after several attempts, I always ended up with unchanged fracB. Is there any custom copy assignment operator, that would do the job? Or the whole approach to immutable objects in C++ is wrong here?
I am aware of const-correctness: https://isocpp.org/wiki/faq/const-correctness
C++11 has deprecated the generation of a copy assignment operator if the class has a userdeclared copy constructor.
So use
Fraction& operator=(const Fraction& rhs) = default;
And remove const here
const int nominator, denominator;
How can const be assignable!?
Note that
Fraction fracC= fracB;//copy constructor calling
Fraction fracC= fracB.Multiply(fracB);//copy constructor calling twice
fracC = fracB.Multiply(fracB);//copy = operator calling
You can now define your own copy-assignment operator for classes that contain const member objects without undefined behavior as of c++20.
This was undefined behavior prior to c++ and remains so for complete const objects but not non-const objects with const members.
https://stackoverflow.com/a/71848927/5282154

Ostream << and operator -= for classes?

I have 2 classes S and M. When I try to use
cout << s3 -= m2;
I get an error that sates:
no operator "-=" matches these operands operand types are: std::ostream -=
class S
{
public:
S& operator-=(M& m)
{
//my code
return *this;
}
}
I tried with 3 parameters, including ostream, but -= has only 2.
How can I fix this?
This has to do with operator precedence. << has a higher precedence than -= so
cout<<s3-=m2;
is treated as
(cout << s3) -= m2;
and not
cout << (s3 -= m2);
You need to use the above form to get what you want.
You have no way to fix this. The operator precedence rules in c++ are fixed and cannot be overloaded.
The only possible solution is to change the using code. For example, if you write
cout << (s3 -= m2);
then your original code should work. Another option is splitting the line in two:
s3 -= m2;
cout << s3;

C++ - Copy Assignment Operator in Template

I'm trying to overload a copy assignment operator in template struct xpair
template <typename First, typename Second>
struct xpair {
First first{};
Second second{};
xpair(){}
xpair (const First& first, const Second& second):
first(first), second(second) {}
xpair& operator= (const xpair& that) {
cout << "*this = " << *this << " " << "that = " << that << endl;
cout << "use operator = " << endl;
*this = that;
return *this;
}
};
But when I test this code with
using commend = string;
using str_str_pair = xpair<string, string>;
using commend_pair = xpair<commend, str_str_pair>;
commend_pair cmd_pair;
str_str_pair str_pair("a", "1");
commend cmd("comment");
cmd_pair.first = cmd;
cmd_pair.second = str_pair;
It gives me infinite output as
use operator =
*this = {,} that = {a,1}
use operator =
*this = {,} that = {a,1}
use operator =
*this = {,} that = {a,1}
use operator =
*this = {,} that = {a,1}
Why is that?
It gives me infinite output as
Why is that?
Because you've defined the function in terms of itself, see the following code comment.
xpair& operator= (const xpair& that)
{
cout << "*this = " << *this << " " << "that = " << that << endl;
cout << "use operator = " << endl;
// Here you're asking for `this` (i.e., an `xpair` type) to be assigned
// a `that` (i.e., another `xpair` type) using the `operator=` which is
// the function currently being implemented/defined. A function calling
// itself is recursion and there is no stopping condition so it will
// continue infinitely.
*this = that;
return *this;
}
Instead your operation should set the data members of this instance using the data members of that instance.
xpair& operator= (const xpair& that)
{
cout << "*this = " << *this << " " << "that = " << that << endl;
cout << "use operator = " << endl;
first = that.first;
second = that.second;
return *this;
}
Your line
*this = that;
is an assignment as well, taking two arguments. And both are xpair<First,Second>, so it calls the same operator again.
What you would probably like to do is this:
this->first = that.first;
this->second = that.second;
which calls the assignment operators for First and Second.
Your problem is, as others have noted, that your operator= calls your operator=. This results in an infinite recursion.
But, I'll argue for a different implementation:
Add this:
template<class Self,
class=std::enable_if_t<std::is_same<std::decay_t<Self>, xpair>{}>
>
friend auto tieme(Self&& self) {
return std::forward_as_tuple(
std::forward<Self>(self).first,
std::forward<Self>(self).second
);
}
to the body of your pair. The enable_if_t stuff is a bit obscure, but it makes sure this free function will only be invoked on genuine xpairs.
Now your operator= is just:
xpair& operator= (const xpair& that) {
tieme(*this)=tieme(that);
return *this;
}
which is nice, because you don't have to repeat the order of your elements twice over.
But it doesn't stop there.
friend bool operator<(const xpair& lhs, const xpair& rhs) {
return tieme(lhs) < tieme(rhs);
}
the same technique lets you write a bunch of other operators. And anyone who has ever had bugs in < boilerplate will understand that the above is nice.
Move assign?
xpair& operator= (xpair&& that) {
tieme(*this)=tieme(std::move(that));
return *this;
}
swap?
friend void swap(xpair& lhs, xpair& rhs) {
std::swap( tieme(lhs), tieme(rhs) );
}
and it scales -- add more stuff to tieme, and it is auto-handled by all your other methods.

Operator Overloading = Negation not working as intended

So I'm working with operator overloading and just realized that my negation operator isn't working as it should be. I'm not exactly sure what I've done wrong.
The .h signature
Vector & Vector::operator-()
The .cpp implementation
Vector & Vector::operator-()
{
pVec[0] = -pVec[0];
pVec[1] = -pVec[1];
pVec[2] = -pVec[2];
return *this;
};
Calling:
cout << "-Vector E = " << -VecE << (-VecE).Magnitude() << endl << endl;
The variables in VecE are like [0, 1 , 1] which means when this is called it should display them as [0, -1, -1] but it's not. So what am I missing?
EDIT: Adding copy constructor and iostream<< overload code:
Vector::Vector(const Vector & Copy)
{
pVec = new double[3];
if (0 == pVec)
{
exit(1);
}
else
{
pVec[0] = Copy.pVec[0];
pVec[1] = Copy.pVec[1];
pVec[2] = Copy.pVec[2];
}
};
ostream & operator<<(ostream & Out, Vector & RHS)
{
cout.precision(1);
Out << fixed << "[ " << RHS.pVec[0] << " " << RHS.pVec[1] << " " << RHS.pVec[2] << " ]" << resetiosflags (ios_base::fixed);
return Out;
};
You need to return a copy of the vector. The way this is written, the expression -VecE will actually modify VecE! Since you evaluate -VecE twice, you are negating the vector twice, and (of course) the negation of the negation is the original value.
To implement this change, you need to alter the operator-() declaration to return a Vector instead of a Vector &.
For example:
Vector Vector::operator-()
{
Vector copy(*this);
copy.pVec[0] = -copy.pVec[0];
copy.pVec[1] = -copy.pVec[1];
copy.pVec[2] = -copy.pVec[2];
return copy;
};
cdhowie is right. You are negating twice.
That said, I don't think you need to change the implementation.
Vector const NegVecE = -VecE;
cout << "-Vector E = " << NegVecE << NegVecE.Magnitude() << endl << endl;
EDIT: As PiotrNycz notes, though this will work, the end state is un-intuitive and therefore the correct solution is to return a copy.
{
int i = 3;
int j = -i; //you would expect i to still be 3 here
}

operator << : std::cout << i << (i << 1);

I use the stream operator << and the bit shifting operator << in one line.
I am a bit confused, why does code A) not produce the same output than code B)?
A)
int i = 4;
std::cout << i << " " << (i << 1) << std::endl; //4 8
B)
myint m = 4;
std::cout << m << " " << (m << 1) << std::endl; //8 8
class myint:
class myint {
int i;
public:
myint(int ii) {
i = ii;
}
inline myint operator <<(int n){
i = i << n;
return *this;
}
inline operator int(){
return i;
}
};
thanks in advance
Oops
Your second example is undefined behavior.
You have defined the << operator on your myint class as if it were actually <<=. When you execute i << 1, the value in i is not modified, but when you execute m << 1, the value in m is modified.
In C++, it is undefined behavior to both read and write (or write more than once) to a variable without an intervening sequence point, which function calls and operators are not, with respect to their arguments. It is nondeterministic whether the code
std::cout << m << " " << (m << 1) << std::endl;
will output the first m before or after m is updated by m << 1. In fact, your code may do something totally bizarre, or crash. Undefined behavior can lead to literally anything, so avoid it.
One of the proper ways to define the << operator for myint is:
myint operator<< (int n) const
{
return myint(this->i << n);
}
(the this-> is not strictly necessary, just my style when I overload operators)
Because int << X returns a new int. myint << X modifies the current myint. Your myint << operator should be fixed to do the former.
The reason you're getting 8 for the first is that apparently m << 1 is called first in your implementation. The implementation is free to do them in any order.
Your << operator is in fact a <<= operator. If you replace the line with
std::cout << i << " " << (i <<= 1) << std::endl; //8 8
you should get 8 8.
since m is a myInt your second example could be rewritten as:
std::cout << m << " " << (m.operator<<( 1)) << std::endl;
The order of evaluation for the subexpressions m and (m.operator<<( 1)) is unspecified, so there's no saying which "m" you'll get for the 1'st expression that m is used in (which is a simple m expression). So you might get a result of "4 8" or you might get "8 8".
Note that the statement doesn't result in undefined behavior because there are sequence points (at least one function call) between when m is modified and when it's 'read'. But the order of evaluation of the subexpressions is unspecified, so while the compiler has to produce a result (it can't crash - at least not legitimately), there's no saying which of the two possible results it should produce.
So the statement is about as useful as one that has undefined behavior, which is to say it's not very useful.
Well (m << 1) is evaluated before m and therefore m holds 8 already, as in your operator<< you overwrite your own value.
This is wrong behaviour on your side, the operator<< should be const and not change your object.
Because the << operator of myint modifies its lhs. So after evaluating m << 1, m will actually have the value 8 (while i << 1 only returns 8, but does not make i equal to 8). Since it is not specified whether m<<1 executes before cout << m (because it's not specified in which order the arguments of a function or operator are evaluated), it is not specified whether the output will be 8 8 or 4 8.
The C++ language does not define the order of evaluation of operators. It only defines their associativity.
Since your results depend on when the operator<< function is evaluated within the expression, the results are undefined.
Algebraic operator $ functions should always be const and return a new object:
inline myint operator <<(int n) const { // ensure that "this" doesn't change
return i << n; // implicit conversion: call myint::myint(int)
}