Overloading Assignment Operator in C++

assignment operator overloading in c++

There are no problems with the second version of the assignment operator. In fact, that is the standard way for an assignment operator.

Edit: Note that I am referring to the return type of the assignment operator, not to the implementation itself. As has been pointed out in comments, the implementation itself is another issue. See here.

overloading assignment operator and header file in c++

You need to declare the function in your header file so you can define it later on.

using namespace std;
#include <stdlib.h>
#include <string>
#include <iostream>
#include "Chemical.h"

class Cabinet{
private:
int rows;
int id_cabinet;
int columns;
Chemical*** chemicals;
string alphabet [9];
public:
Cabinet(int id = 0, int rows = 0, int columns = 0);
Cabinet& operator=( const Cabinet& right );
~Cabinet();
int getRow();
int getColumn();
int plusCount();

};

C++ overload assignment operator

The line SpecialFloat f = 1.0f; cannot perform assignment from 1.0f to f because f doesn't exist yet. We are just creating it.

It would do if you had written SpecialFloat f{0.0f}; f = 1.0f [Demo].

The line SpecialFloat f = 1.0f; is doing copy initialization (1).

Initializes an object from another object.

Syntax

T object = other; (1)

In your code T is SpecialFloat, a class type, and other is a float (not T or derived from T).

The effects of copy initialization are:

...

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T [...] user-defined conversion sequences that can convert from the type of other to T are
examined and the best one is selected through overload resolution. The result of the conversion, which is a rvalue temporary [...] of the cv-unqualified version of T if a converting constructor was used, is then used to direct-initialize the object.

User-defined conversions from float to SpecialFloat should be examined. However, explicit constructors are not considered for copy-initialization.

Notes

Copy-initialization is less permissive than
direct-initialization: explicit constructors are not converting
constructors and are not considered for copy-initialization.

One way to solve this is to use direct initialization, and, if possible, with braces instead of parentheses, i.e. SpecialFloat f{1.0f}; [Demo].
There's a C++ Core Guideline advising about preferring the {}-initializer syntax.
Also, declaring single-argument constructors explicit is a general recommendation, so I would keep the user-declared constructor as explicit.

Another way would be to make SpecialFloat class an aggregate, by removing the user-declared constructor, and use aggregate initialization, SpecialFloat f = {1.0f}; [Demo].

Finally, as commented by others, notice the signature of the assignment operator is SpecialFloat& operator=(const float f), what indicates that a SpecialFloat& has to be returned. So first, update the object with m_float = f;; then, return it with return *this;.


[Edit]

I just came accross this article from Arthur O'Dwyer's, The Knightmare of Initialization in C++ where he basically favours copy initialization over direct initialization with braces, in order to improve the readability of the code.

Simple guidelines for variable initialization in C++:

  • Use = whenever you can.
  • Use initializer-list syntax {} only for element initializers (of containers and aggregates).
  • Use function-call syntax () to call a constructor, viewed as an object-factory.

Thus:

int i = 0;
std::vector<int> v = {1, 2, 3, 4};
Widget w(name, price, quantity);

Moreover, he suggests to combine the copy initialization with the Almost Always Auto style. Going back to the original OP's question, that would allow us to keep the SpecialFloat class untouched and write auto f = SpecialFloat{1.0f}; [Demo].

He acknowledges though that his guidelines conflict with the aforementioned C++ Core Guideline of preferring the {}-initializer syntax.

overloading copy assignment operator with classes without a reference parameter name

How does this copy assignment operator work?

Redacted:

Base& operator=(Base const& rhs) {
cout << "Base operator=\n";
value = rhs.value;
return *this;
}

The assignment operator is just a function call. In this particular class's case, the compiler will synthesize one that does the right thing, but if you want the cout side-effect for educational purposes you've done the right thing.

You could also call it in a method function style:

b.operator=(b1);

The b = b1; is just syntactic sugar for the above.

The assignment operator should be self-assignment safe. But general guidance is to make it safe without checking for the self-assignment case explicitly, which is "bad" because it optimizes for the pathological case.

In your implementation, it is safe without the self-assignment check.

I prefer specifying the qualifier in Base const& order rather than const Base& order, because the general rule is "the qualifier always binds to the thing to its immediate left", and the exception to the general rule is "...unless the qualifier comes first, in which case it then binds to the thing to its immediate right." That becomes a problem when people have the grasped the exception to the rule, but not the general rule, and then have troubles parsing Base*const* in their heads.

Does the reference before the operator keyword need a parameter name?

It's name is *this. It is a return type, not a parameter, so it does not have a parameter name.

Some people have troubles with "Why does C++ use this as a pointer to itself, rather than as a reference to itself."

It is a historical anomaly from the evolution of C++. Originally, there was a this pointer, and references had not been added to the language yet.

With 20/20 hindsight, this would have been a reference. But that ship has sailed.

In my own code, only to help make it more legible, I'll do:

auto& self = *this;
self[i] = 5;

...rather than the more confusing (imo)...

(*this)[i] = 5;

Overloading assignment operator in C++

Not returning a reference is a waste of resources and a yields a weird design. Why do you want to do a copy for all users of your operator even if almost all of them will discard that value?

a = b; // huh, why does this create an unnecessary copy?

In addition, it would be surprising to users of your class, since the built-in assignment operator doesn't copy likewise

int &a = (some_int = 0); // works

Overload assignment operator and rule of zero

According to my understanding, adding a operator= overload will not prevent the compiler from generating the default one according to the rule of 0.

I base this understanding on the fact that your operator= overload is not in fact a copy assignment, nor a move assignment.

Therefore the rules about generaing default constructors and assignment operators are not relevant.

I verified it with MSVC.

You can use the code below to verify with your compiler:

#include <iostream>

template <typename T>
struct B
{
B(T const & n) : bn(n) {}
T bn{ 0 };
};

template <typename T>
struct A
{
A(T const & n) : an(n) {}
A<T>& operator=(const B<T>& rhs)
{
an = rhs.bn;
return *this;
}
T an{ 0 };
};

int main()
{
A<int> a1{ 5 };
A<int> a2{ 6 };
std::cout << a2.an << ",";
a2 = a1; // Use default assinment
std::cout << a2.an << ",";
B<int> b{ 3 };
a2 = b; // Use custom assignment
std::cout << a2.an << std::endl;
return 0;
}

The output should be: 6,5,3:

6 is the value A<int> a2 is constructed with, 5 is the value assigned from A<int> a1, and 3 is the value assigned from B<int> b.

Note: an alternative would be to use a user-defined conversion function, as @LouisGo commented (see above).

Overloading copy assignment operator in c++

Type b = a;

is not assignment. Is is a variable definition with initialization and causes the copy constructor of Type to be called with a as argument. Constructors don't have return values and as you correctly note it wouldn't make sense for Type b = a to have any result value, because it isn't even an expression.

However

b = a;

(with prior Type B;) is assignment. Assignment is an expression, so it has a result value that can be used in other expressions. For example the following is valid:

int a = 0;

(a = 2) += 1;

and will set a to 3. Here for fundamental types such as int, the assignment returns a reference to the left-hand side of the assignment.

With class types this is mimicked by having the copy assignment operator return a reference to the left-hand side of the assignment (i.e. *this) as well.

One typical use case for this is assigning to multiple instances:

Type a, b, c;
// Do something with c
a = b = c;

After the last line all three a, b and c will be in the same state, that of c prior to the change assignment, because the expression is parsed as a = (b = c); and the right-hand assignment returns a reference to b which is used to assign to a after c was assigned to b.

It is not required to return a reference to *this from an overloaded assignment operator. Doing is convention however, because it enables this particular use case. If you do not return a reference to *this users of your class may be surprised when that idiom suddenly doesn't work for your class, even if it may not be often used.

A value returned from a function (or operator) does not need to be used. If you write

b = a;

the returned reference is simply discarded. It is never stored anywhere and that is not a problem at all.

c++ overloading assignment operator of another class

If you want the ability to assign a double to fraction you can declare a non-explicit constructor:

fraction(double d = 0, double n = 1) : d(d), n(n) {}

and for reverse

operator double() const { return n/d; }

and then

fraction f;

f = 12.5; // Assign a double to fraction

double x = f; // Assign fraction as double to a double

Purpose of assignment operator overloading in C++

Actually, after seeing juanchopanza's answer (which was deleted), I think I ended up figuring it out myself.

Copy-assignment operators allow classes like basic_string to avoid allocating resources unnecessarily when they can re-use them (in this case, memory).

So when you assign to a basic_string, an overloaded copy assignment operator would avoid allocating memory, and would just copy the string data directly to the existing buffer.

If the object had to be destroyed and constructed again, the buffer would have to be reallocated, which would be potentially much more costly for a small string.

(Note that vector could benefit from this too, but only if it knew that the elements' copy constructors would never throw exceptions. Otherwise it would need to maintain its exception safety and actually perform a copy-and-swap.)



Related Topics



Leave a reply



Submit