C++ - Copy Assignment Operator in Template - c++

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.

Related

How to call a function several times in the same line using an operator

This function adds a TElement to a vector<TElement>:
friend void operator<<(ToDo& todu, const ElementT& D) {
todu.add(D);
return;
}
I want to call it like this:
todo << elem1 << elem2 << elem3 << elem4...
If you want to chain operations like that, then you can simply return the ToDo parameter by reference, like this:
friend ToDo& operator<<(ToDo& todu, const ElementT& D)
{
todu.add(D);
return todu;
}
and now you should be able to use operator<< like this:
todo << elem1 << elem2 << elem3 << elem4;

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

C++: Parent object change their id attribute without method call

As I specified in the title, this is a problem that puzzled me enormously, and still I can not find a fix for it.
I created a Point class which has three private attributes (_id, _x and _y) with related public get and set methods.
For example, I created three points (G, H, R), where the point G is a "parent". Point H is the prototype of the point G (a clone) and point R will be the outcome of the meeting points (G+H) (addition of the points).
Points sample:
Point G ("pG", 3.5f, 6.5f);
Point H (G.prototype ()); H.setId ("pH");
Point R;
R = G + H;
The program works correctly, but unfortunately after the operation {R=G+H}, _id attribute of point G becomes the clone attribute of point R, I do not understand why this is happening because the evaluation is done from right to left (H+, +G, =R).
The question is why G point attribute changes itself?
Code where the problem occurs:
#include <iostream>
#include <string>
using namespace std;
class Point
{
private:
string _id;
double _x;
double _y;
public:
Point(string id="undefined", double x = 0.00f, double y = 0.00f)
:_id(id), _x(x), _y(y){}
~Point(){}
public:
// Getters methods
string getId() const { return _id;}
double getX() const { return _x; }
double getY() const { return _y; }
void setId(string id) { _id = id; }
// Setters methods
void setX(double n = 0.00f) { _x = n; }
void setY(double n = 0.00f) { _y = n; }
Point prototype() { return Point(_id, _x, _y); }
Point operator+ (const Point& p)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
_id.append("+").append(p._id),
_x+p._x,
_y+p._y
);
}
Point operator= (const Point& p)
{
cout << "Operator = called, returning a new object Point(p._id, p._x, p._y)" << endl;
_id=p._id;
_x=p._x;
_y=p._y;
return *this;
}
};
int main()
{
Point G("G",10.0f, 10.0f);
Point H(G.prototype()); H.setId("H");
Point R;
R = G + H;
cout << "object Point {id: " << G.getId() << ", x: " << G.getX() << ", y: " << G.getY() << "}" << endl;
cout << "object Point {id: " << H.getId() << ", x: " << H.getX() << ", y: " << H.getY() << "}" << endl;
cout << "object Point {id: " << R.getId() << ", x: " << R.getX() << ", y: " << R.getY() << "}" << endl;
return 0;
}
Thanks in advance!
Point operator+ (const Point& p)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
_id.append("+").append(p._id), // HERE
_x+p._x,
_y+p._y
);
}
In this line, you modify _id property of this object. As a rule of thumb, binary + operator should be a const member or - IMO cleaner - a static method taking two const reference arguments.
BTW, you can add strings in C++ by just applying + operator to them (_id + "+" + p._id).
If you implement your binary arithmetic operators as members, you should consider making the operator a const member. This would catch the obvious modification in your implementation (the implementation is made out of line to avoid including unrelated code):
Point Point::operator+ (const Point& p) const
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
_id.append("+").append(p._id),
_x+p._x,
_y+p._y
);
}
The operation _id.append("+") actually operates on this->_id, i.e., the _id member of the left hand operand. Since you member operator isn't const, the compiler lets you do the modification. This is probably not what you intended to do. You probably rather wanted to write:
Point Point::operator+ (const Point& p) const
{
cout << "Operator + called, returning a new object\n";
return Point
(
_id + "+" + p._id,
_x+p._x,
_y+p._y
);
}
... which creates a suitable temporary string with the desired value (I also replaced the excessive use of std::endl).
This line in the operator+ overload:
_id.append("+").append(p._id),
You have to be very careful here, you are currently in an object (in your example this would be the G object). append actually alters the local string, so your code is appending + then p._id then deep copying that to the returned value.
A quick fix is to change this to a temporary that holds what you need:
_id + "+" + p._id
To avoid problems like this in the future, you should declare operator overloads as a friend method so it's extremely clear what you're operating on.
friend Point operator+ (const Point& lhs, const Point& rhs)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
lhs._id.append("+").append(rhs._id), //COMPILER ERROR! We're modifying the const "lhs"!
lhs._x+rhs._x,
lhs._y+rhs._y
);
}
So let's change this to:
friend Point operator+ (const Point& lhs, const Point& rhs)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
lhs._id + "+" + rhs._id, // no longer modifying lhs -- all is well!
lhs._x+rhs._x,
lhs._y+rhs._y
);
}

operator definition not working right

I am trying to implement a comparison operator but I'm getting the following errors
whole.cpp(384): error C2270: '==' : modifiers not allowed on nonmember functions
whole.cpp(384): error C2805: binary 'operator ==' has too few parameters
whole.cpp(384): error C2274: 'function-style cast' : illegal as right side of '.' operator
I can't seem to pin down the problem though so here is the code
this is the operator implementation in the class
bool operator==(const DateC& p) const
{
return ( DateC::DateC()== p.DateC() );
};
#include <assert.h>
int main(unsigned int argc, char* argv[])
{
DateC f(29,33,11);
DateC::testAdvancesWrap();
};
void DateC::testAdvancesWrap(void)
{
DateC d;
cout << "DateC::testAdvanceWrap()" << endl ;
cout << "*********************" << endl << endl ;
cout << "\tCHECK ADVANCE MULTIPLES:" << endl;
cout << "\t------------------------" << endl;
d.setDay(1);
d.setMonth(12);
d.setYear(1999);
prettyPrint(d);
cout << "ACTION: set date 01-Dec-1999, advance, 31 days, 1 month and 1 year ->" << endl;
d.advance(1,1,31);
assert( d == DateC(1,2,2001) );
cout << "SUCCESS" << endl;
prettyPrint(d);
cout << endl << endl;
}
the rest of the functions working fine it's only the assert()
You can implement comparison operators as member functions or free functions. To implement it as a free function as you are trying to do you need to accept two arguments - the value on the left hand side of = and the value on the right hand side of =. The example below shows how to properly do this.
struct Date
{
int variable_;
};
bool operator==(const Date& lhs, const Date& rhs)
{
return lhs.variable_ == rhs.variable_;
}
To implement the comparison operator as a member function you only need to take one argument which is the value on the right hand size of =. The object owning the comparison operator being executed is the value on the left hand side of =. In this case the operator should be const qualified.
struct Date
{
int variable_;
bool operator==(const Date& rhs) const
{
return variable_ == rhs.variable_;
}
};
In all cases the argument should be taken as a const reference to allow the use of rvalues (temporaries).

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
}