Value parameters to operator= cause strange compilation error - c++

I'm trying to relearn C++, and I tend to want to have an intricate understanding of how everything works (not just "how do I do this"). So I'm wondering why this produces the error it does. Yes, I know that an overloaded assignment operator is supposed to use references (and it works fine if I do), but I'm hoping that an answer to this question might help me learn more about the language rules.
class some_class {
public:
int n1;
some_class(int z) : n1(z) { }
some_class(some_class &x) : n1(x.n1) { }
some_class operator= (some_class x) { n1 = x.n1; return *this; }
// some_class & operator= (some_class & x) { n1 = x.n1; return *this; } works fine
};
main () {
some_class a(10);
some_class b(20);
some_class c(30);
c = b = a; // error here
}
The compiler (C++03) gives me this, on the c = b = a line:
In function 'int main()':
error: no matching function for call to 'some_class::some_class(some_class)'
note: candidates are: some_class::some_class(some_class&)
note: some_class::some_class(int)
error: initializing argument 1 of 'some_class some_class::operator=(some_class)'
It's confusing to me, because b = a works fine, and it's looking for a constructor that I'm not legally allowed to declare. I realize that in c = b = a, the b = a part returns a value (not a reference), and that may result in the result being copied to a temporary. But why would c = <temporary> result in a compilation error when b = a wouldn't? Any idea what's going on?

Your copy constructor has a non-const reference as its parameter. Temporaries can't be bound to non-const references. When you do:
c = b = a;
This is equivalent (as you say) to:
c.operator=(<temporary>);
It therefore tries to invoke your copy constructor with a temporary whilst initialising the first argument of the call to operator=. This fails for the reason mentioned. A sensible way to fix it is to change the signature of operator= to the more conventional:
some_class& operator=(const some_class& x);
The copy constructor would not then be needed in the implementation of operator=, since the argument to operator= would not be copied. However, copy constructors should generally take a const reference parameter, so you should also change the signature of the copy constructor to:
some_class(const some_class& x);

What is causing the error is your copy constructor, which should be
some_class(const some_class&)
This is because you cannot pass a temporary object to a non-const reference, which is what happens in your chained assignment. This is because your assignment operator returns by value which creates a temporary object, which is then passed to the next assignment operator as a value parameter. This invokes the copy constructor, which has a non-const reference parameter and so cannot bind to the temporary object.
Assignment operator should be
some_class& operator= (some_class x)
or
some_class& operator= (const some_class& x)
That is, it takes a value or const reference paramater of the same type and returns a non-const reference, which is *this. Alternatively it can have void return type to prevent chaining.
I know other variations are "allowed", but don't use them unless you know what you are doing.
Unless you have a reason for a value parameter (copy-and-swap idiom for example), you should use const reference to prevent an extra copy.

some_class(some_class &x) : n1(x.n1) { }
some_class operator= (some_class x) { n1 = x.n1; return *this; }
should be
some_class(const some_class &x) : n1(x.n1) { }
some_class& operator=(const some_class& x) { n1 = x.n1; return *this; }
Using your original signatures, you can't copy or assign from const temporary objects as you show in your sample.

The key issue is also with unnecessary copy of data if we are not working with references.
There is one more way to solve this, given that you are trying to relearn c++, it would be interesting to know. C++11 have introduced r-value references and std::move to allow working with temporaries in copy constructor and assignment operators to reduce memory copies.
Move copy constructor and assignment operator would look like
some_class(some_class &&x) : n1(std::move(x.n1)) {}
some_class& operator=(some_class&& x) { n1 = std::move(x.n1); return *this; }

In the statement:
c = b = a;
As assignment operator has Right-to-Left associativity, b = a, is executed first. Since assignment operator returns value, copy constructor is invoked. Since temporary objects cannot be referred to by non-const references (as Stuart Golodetz already mentioned above), the compiler looks for some_class::some_class(some_class x) kind of constructor; and is complaining on not finding it.
The compilation succeeds only if you modify the signature of the copy constructor to:
some_class(const some_class& x);
Assignment operator, as others mentioned, returns a reference traditionally, for the reason that we could chain several of them. It is however not mandatory to design them so.

Related

What can be done to prevent misleading assigment to returned value?

After many years of using C++ I realized a quirk in the syntax when using custom classes.
Despite being the correct language behavior it allows to create very misleading interfaces.
Example here:
class complex_arg {
double r_;
double phi_;
public:
std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};
int main() {
complex_arg ca;
ca.value() = std::complex<double>(1000., 0.); // accepted by the compiler !?
assert( ca.value() != std::complex<double>(1000., 0.) ); // what !?
}
https://godbolt.org/z/Y5Pcjsc8d
What can be done to the class definition to prevent this behavior?
(Or at the least flag the user of the clas that the 3rd line is not really doing any assignment.)
I see only one way out, but it requires modifying the class and it doesn't scale well (to large classes that can be moved).
const std::complex<double> value() const;
I also tried [[nodiscard]] value() but it didn't help.
As a last resort, maybe something can be done to the returned type std::complex<double> ? (that is, assuming one is in control of that class)
Note that I understand that sometimes one might need to do (optimized) assign to a newly obtained value and passe it to yet another function f( ca.value() = bla ). I am not questioning this usage per se (although it is quite confusing as well); I have the problem mostly with ca.value() = bla; as a standalone statement that doesn't do what it looks.
Ordinarily we can call a member function on an object regardless of whether that object's value category is an lvalue or rvalue.
What can be done to the class definition to prevent this behavior?
Prior to modern C++ there was no way prevent this usage. But since C++11 we can ref-qualify a member function to do what you ask as shown below.
From member functions:
During overload resolution, non-static member function with a cv-qualifier sequence of class X is treated as follows:
no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument
lvalue ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X
rvalue ref-qualifier: the implicit object parameter has type rvalue reference to cv-qualified X
This allows us to do what you ask for a custom managed class. In particular, we can & qualify the copy assignment operator.
struct C
{
C(int)
{
std::cout<<"converting ctor called"<<std::endl;
}
C(){
std::cout<<"default ctor called"<<std::endl;
}
C(const C&)
{
std::cout<<"copy ctor called"<<std::endl;
}
//-------------------------v------>added this ref-qualifier
C& operator=(const C&) &
{
std::cout<<"copy assignment operator called"<<std::endl;;
return *this;
}
};
C func()
{
C temp;
return temp;
}
int main()
{
//---------v---------> won't work because assignment operator is & qualified
func() = 4;
}

C++ an issue with returning a temporary object

Hello I'm about a month old in C++ so please excuse me if this question is too trivial.
#include <iostream>
using namespace std;
class Point {
int x,y;
public:
Point(int x_val = 0,int y_val = 0) :x(x_val),y(y_val) { }
Point(Point& copy) : x(copy.x), y(copy.y) { }
void showLocation() const { cout << "[" << x << "," << y << "]" << endl; }
friend Point operator+(const Point& p1, const Point& p2);
friend Point operator-(const Point& p1, const Point& p2);
Point operator-(Point& p2) { // (1)
Point(x-p2.x, y-p2.y).showLocation();
return Point(x-p2.x, y-p2.y);
}
};
Point operator+(const Point& p1, const Point& p2) { // (2)
Point pos(p1.x+p2.x, p1.y+p2.y);
return pos;
}
Point operator-(const Point& p1, const Point& p2) { // (3)
return Point(p1.x-p2.x, p1.y-p2.y);
}
int main() {
Point p1(3,4);
Point p2(2,5);
(p1+p2).showLocation();
//(p1-p2).showLocation();
operator-(p1,p2);
}
So this is a simple code for practicing operator overloading - I simply created a point and overloaded + and - operators, both as a member of the class and a global function.
When I compile this code, however, I discovered that while (2) works, both (1) and (3) keep showing the error that I cannot see why:
q1.cpp:17:10: error: no matching constructor for initialization of
'Point'
return Point(x-p2.x, y-p2.y);
^~~~~~~~~~~~~~~~~~~~~ q1.cpp:8:2:
note: candidate constructor not viable: no known conversion from 'Point' to 'int' for 1st argument
As I understand it, both (1) and (3) are supposed to return a temporary Point object, which, according to Google search, should be equivalent to case (2).
Also, the error statement confuses me even more - I can't see any conversion occurring in the expression mentioned as problematic.
Point(x-p2.x, y-p2.y).showLocation();
This works fine, and case (2) also does, so I guess this is not a syntax issue. I couldn't see any problem other than the possibility of an issue regarding returning a temporary object (without naming it).
Thanks!
You can fix this by simply removing your copy constructor. It serves no useful purpose. Alternatively, you can fix your copy constructor by using const in the signature as per the default signature.
Point(Point const & copy) : x(copy.x), y(copy.y) { }
Without the const, the copy constructor cannot be used with a temporary instance as input.
Resources:
http://en.cppreference.com/w/cpp/language/copy_constructor
c++ copy constructor signature : does it matter
This:
Point(Point& copy) : x(copy.x), y(copy.y) { }
is a technically valid copy constructor, but it restricts the possible actual arguments.
A temporary or const object can't be bound to the Point& formal argument, because that's a reference to non-const, which would permit modifying the actual argument.
Instead you can write
Point( Point const& other ): x( other.x ), y( other.y ) {}
where I've also corrected the misleading argument naming.
But even easier, for this class you can just do
Point( Point const& other ) = default;
And absolutely easiest, just don't define a copy constructor at all, let the compiler do it for you.
Similarly, for maximum usability you should change
Point operator-(Point& p2) { // (1)
to
Point operator-(Point const& p2) const { // (1)
The const for the argument means that it can now be called with const or temporary (rvalue) argument.
And the const for the member function itself, the last const, means that it can be called on a const object. The rules here are a bit subtle. In contrast to the restriction for the argument, without the const it can already be called on a temporary object (i.e. via an rvalue-expression).
General advice:
sprinkle const liberally just about everywhere it can be used,
let the compiler generate copy operations unless you're taking charge of copying, and
preferentially use data member of types where copying is safe, e.g. built-in types, smart pointers and standard library collections.
The reason for the error (in your case (1)) is
Point operator-(Point& p2) { // (1)
Point(x-p2.x, y-p2.y).showLocation();
return Point(x-p2.x, y-p2.y);
}
is that the Point(x-p2.x, y - p2.y) produces a temporary object. Returning that (semantically) requires creating a copy of the temporary, which requires a copy constructor that accepts a const argument. Your copy constructor does not accept a const argument.
The solutions are to either modify your copy constructor to accept a const parameter, or remove it completely (since the compiler will automatically generate a copy constructor that works correctly for you). In C++11, use Point(const Point &) = default; if needed, to force the compiler to generate the correct copy constructor.
Note: the standard requires the compiler to enforce the need for the correct copy constructor, even if the temporary is optimised out of existance. This helps improve portability of code to compilers that do not optimise out the temporary.
Another concern with your code is that you have two variants of operator-().
Your cases (1) and (3) are not equivalent. The first generates a member function form of operator-() (which accepts one argument, since there is an implied this) and the second is a non-member form which accepts two arguments.
When it sees an expression like p1-p2 (where p1 and p2 are both of type Point), both versions of that operator-() are equally good candidates. The compiler will complain about ambiguity, since it has no reason to prefer one over the other. So a statement
(p1-p2).showLocation();
will not compile. I assume you have commented it out for that reason.
The solution to that is to use one form or the other of operator-(). Not both at once.

Should the assignment operator observe the assigned object's rvalueness?

For class types it is possible to assign to temporary objects which is actually not allowed for built-in types. Further, the assignment operator generated by default even yields an lvalue:
int() = int(); // illegal: "expression is not assignable"
struct B {};
B& b = B() = B(); // compiles OK: yields an lvalue! ... but is wrong! (see below)
For the last statement the result of the assignment operator is actually used to initialize a non-const reference which will become stale immediately after the statement: the reference isn't bound to the temporary object directly (it can't as temporary objects can only be bound to a const or rvalue references) but to the result of the assignment whose life-time isn't extended.
Another problem is that the lvalue returned from the assignment operator doesn't look as if it can be moved although it actually refers to a temporary. If anything is using the result of the assignment to get hold of the value it will be copied rather than moved although it would be entirely viable to move. At this point it is worth noting that the problem is described in terms of the assignment operator because this operator is typically available for value types and returns an lvalue reference. The same problem exists for any function returning a reference to the objects, i.e., *this.
A potential fix is to overload the assignment operator (or other functions returning a reference to the object) to consider the kind of object, e.g.:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
The possibility to overload the assignment operators as above has come with C++11 and would prevent the subtle object invalidation noted above and simultaneously allow moving the result of an assignment to a temporary. The implementation of the these two operators is probably identical. Although the implementation is likely to be rather simple (essentially just a swap() of the two objects) it still means extra work raising the question:
Should functions returning a reference to the object (e.g., the assignment operator) observe the rvalueness of the object being assigned to?
An alternatively (mentioned by Simple in a comment) is to not overload the assignment operator but to qualify it explicitly with a & to restrict its use to lvalues:
class GG {
public:
// other members
GG& operator=(GG) & { /*...*/ return *this; }
};
GG g;
g = GG(); // OK
GG() = GG(); // ERROR
IMHO, the original suggestion by Dietmar Kühl (providing overloads for & and && ref-qualifiers) is superior than Simple's one (providing it only for &).
The original idea is:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
and Simple has suggested to remove the second overload. Both solutions invalidate this line
G& g = G() = G();
(as wanted) but if the second overload is removed, then these lines also fail to compile:
const G& g1 = G() = G();
G&& g2 = G() = G();
and I see no reason why they shouldn't (there's no lifetime issue as explained in Yakk's post).
I can see only one situation where Simple's suggestion is preferable: when G doesn't have an accessible copy/move constructor. Since most types for which the copy/move assignment operator is accessible also have an accessible copy/move constructor, this situation is quite rare.
Both overloads take the argument by value and there are good reasons for that if G has an accessible copy/move constructor. Suppose for now that G does not have one. In this case the operators should take the argument by const G&.
Unfortunately the second overload (which, as it is, returns by value) should not return a reference (of any type) to *this because the expression to which *this binds to is an rvalue and thus, it's likely to be a temporary whose lifetime is about to expiry. (Recall that forbidding this from happening was one of the OP's motivation.)
In this case, you should remove the second overload (as per Simple's suggestion) otherwise the class doesn't compile (unless the second overload is a template that's never instantiated). Alternatively, we can keep the second overload and define it as deleted. (But why bother since the existence of the overload for & alone is already enough?)
A peripheral point.
What should be the definition of operator = for &&? (We assume again that G has an accessible copy/move constructor.)
As Dietmar Kühl has pointed out and Yakk has explored, the code of the both overloads should be very similar and, in this case, it's better to implement the one for && in terms of the one for &. Since the performance of a move is expected to be no worse than a copy (and since RVO doesn't apply when returning *this) we should return std::move(*this). In summary, a possible one-line definition is:
G operator =(G o) && { return std::move(*this = std::move(o)); }
This is good enough if only G can be assigned to another G or if G has (non-explicit) converting constructors. Otherwise, you should instead consider giving G a (template) forwarding copy/move assignment operator taking an universal reference:
template <typename T>
G operator =(T&& o) && { return std::move(*this = std::forward<T>(o)); }
Although this is not a lot of boiler plate code it's still an annoyance if we have to do that for many classes. To decrease the amount of boiler plate code we can define a macro:
#define ASSIGNMENT_FOR_RVALUE(type) \
template <typename T> \
type operator =(T&& b) && { return std::move(*this = std::forward<T>(b)); }
Then inside G's definition one adds ASSIGNMENT_FOR_RVALUE(G).
(Notice that the relevant type appears only as the return type. In C++14 it can be automatically deduced by the compiler and thus, G and type in the last two code snippets can be replaced by auto. It follows that the macro can become an object-like macro instead of a function-like macro.)
Another way of reducing the amount of boiler plate code is defining a CRTP base class that implements operator = for &&:
template <typename Derived>
struct assignment_for_rvalue {
template <typename T>
Derived operator =(T&& o) && {
return std::move(static_cast<Derived&>(*this) = std::forward<T>(o));
}
};
The boiler plate becomes the inheritance and the using declaration as shown below:
class G : public assignment_for_rvalue<G> {
public:
// other members, possibly including assignment operator overloads for `&`
// but taking arguments of different types and/or value category.
G& operator=(G) & { /*...*/ return *this; }
using assignment_for_rvalue::operator =;
};
Recall that, for some types and contrarily to using ASSIGNMENT_FOR_RVALUE, inheriting from assignment_for_rvalue might have some unwanted consequences on the class layout.
The first problem is that this is not actually ok in C++03:
B& b = B() = B();
in that b is bound to an expired temporary once the line is finished.
The only "safe" way to use this is in a function call:
void foo(B&);
foo( B()=B() );
or something similar, where the line-lifetime of the temporaries is sufficient for the lifetime of what we bind it to.
We can replace the probably inefficient B()=B() syntax with:
template<typename T>
typename std::decay<T>::type& to_lvalue( T&& t ) { return t; }
and now the call looks clearer:
foo( to_lvalue(B()) );
which does it via pure casting. Lifetime is still not extended (I cannot think of a way to manage that), but we don't construct to objects then pointlessly assign one to the other.
So now we sit down and examine these two options:
G operator=(G o) && { return std::move(o); }
G&& operator=(G o) && { *this = std::move(o); return std::move(*this); }
G operator=(G o) && { *this = std::move(o); return std::move(*this); }
which are, as an aside, complete implementations, assuming G& operator=(G o)& exists and is written properly. (Why duplicate code when you don't need to?)
The first and third allows for lifetime extension of the return value, the second uses the lifetime of *this. The second and third modify *this, while the first one does not.
I would claim that the first one is the right answer. Because *this is bound to an rvalue, the caller has stated that it will not be reused, and its state does not matter: changing it is pointless.
The lifetime of first and third means that whomever uses it can extend the lifetime of the returned value, and not be tied to whatever *this's lifetime is.
About the only use the B operator=(B)&& has is that it allows you to treat rvalue and lvalue code relatively uniformly. As a downside, it lets you treat it relatively uniformly in situations where the result may be surprising.
std::forward<T>(t) = std::forward<U>(u);
should probably fail to compile instead of doing something surprising like "not modifying t" when T&& is an rvalue reference. And modifying t when T&& is an rvalue reference is equally wrong.

Should I still return const objects in C++11? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Should I return const objects?
(The original title of that question was: int foo() or const int foo()? explaining why I missed it.)
Effective C++, Item 3: Use const whenever possible. In particular, returning const objects is promoted to avoid unintended assignment like if (a*b = c) {. I find it a little paranoid, nevertheless I have been following this advice.
It seems to me that returning const objects can degrade performance in C++11.
#include <iostream>
using namespace std;
class C {
public:
C() : v(nullptr) { }
C& operator=(const C& other) {
cout << "copy" << endl;
// copy contents of v[]
return *this;
}
C& operator=(C&& other) {
cout << "move" << endl;
v = other.v, other.v = nullptr;
return *this;
}
private:
int* v;
};
const C const_is_returned() { return C(); }
C nonconst_is_returned() { return C(); }
int main(int argc, char* argv[]) {
C c;
c = const_is_returned();
c = nonconst_is_returned();
return 0;
}
This prints:
copy
move
Do I implement the move assignment correctly? Or I simply shouldn't return const objects anymore in C++11?
Returning const objects is a workaround that might cause other problems. Since C++11, there is a better solution for the assignment issue: Reference Qualifiers for member functions. I try to explain it with some code:
int foo(); // function declaration
foo() = 42; // ERROR
The assignment in the second line results in a compile-time error for the builtin type int in both C and C++. Same for other builtin types. That's because the assignment operator for builtin types requires a non-const lvalue-reference on the left hand side. To put it in code, the assignment operator might look as follows (invalid code):
int& operator=(int& lhs, const int& rhs);
It was always possible in C++ to restrict parameters to lvalue references. However, that wasn't possible until C++11 for the implicit first parameter of member functions (*this).
That changed with C++11: Similar to const qualifiers for member functions, there are now reference qualifiers for member functions. The following code shows the usage on the copy and move operators (note the & after the parameter list):
struct Int
{
Int(const Int& rhs) = default;
Int(Int&& rhs) noexcept = default;
~Int() noexcept = default;
auto operator=(const Int& rhs) & -> Int& = default;
auto operator=(Int&& rhs) & noexcept -> Int& = default;
};
With this class declaration, the assignment expression in the following code fragment is invalid, whereas assigning to a local variable works - as it was in the first example.
Int bar();
Int baz();
bar() = baz(); // ERROR: no viable overloaded '='
So there is no need to return const objects. You can restrict the assigment operators to lvalue references, so that everything else still works as expected - in particular move operations.
See also:
What is "rvalue reference for *this"?
Returning a const object by value arguably was never a very good idea, even before C++11. The only effect it has is that it prevents the caller from calling non-const functions on the returned object – but that is not very relevant given that the caller received a copy of the object anyway.
While it is true that being returned a constant object prevents the caller from using it wrongly (e.g. mistakenly making an assignment instead of a comparison), it shouldn't be the responsibility of a function to decide how the caller can use the object returned (unless the returned object is a reference or pointer to structures the function owns). The function implementer cannot possibly know whether the object returned will be used in a comparison or for something else.
You are also right that in C++11 the problem is even graver, as returning a const effectively prevents move operations. (It does not prevent copy/move elision, though.)
Of course it is equally important to point out that const is still extremely useful (and using it no sign of paranoia) when the function returns a reference or a pointer.
The reason that your call to const_is_returned triggers copy constructor rather than move constructor is the fact that move must modify the object thus it can't be used on const objects. I tend to say that using const isn't advised in any case and should be subjected to a programmer judgement, otherwise you get the things you demonstrated. Good question.

why must you provide the keyword const in operator overloads

Just curious on why a param has to be a const in operation overloading
CVector& CVector::operator= (const CVector& param)
{
x=param.x;
y=param.y;
return *this;
}
couldn't you have easily done something like this ??
CVector& CVector::operator= (CVector& param) //no const
{
x=param.x;
y=param.y;
return *this;
}
Isn't when something becomes a const, it is unchangeable for the remainder of the applications life ?? How does this differ in operation overloading ???
You don't need const:
#numerical25: Just curious on why a param has to be a const in operation overloading
It's not required, but it is a good design decision.
See the C++ standard Section 12.8-9:
A user-declared copy assignment
operator X::operator= is a non-static
non-template member function of class
X with exactly one parameter of type
X, X&, const X&, volatile X& or const
volatile X&
I think it's a good idea though:
Using a const parameter does seems like a logical design decision to me though because you want to ensure that the other value will not be changed.
It tells other people that use your class that you will not be changing the other value when you say something like: myObject = other; and it enforces this so you can't accidentally change other.
Also if you allowed non const references to the object as the parameter, then you are limiting the amount of objects that can use your function. If it is const it can be used for parameters that are const and non const. If your parameter is non const it can only be used by parameters that are non const.
const only applies to the current reference, not the object:
#numerical25: Isn't when something becomes a const, it is unchangeable for the remainder of the applications life ?? How does this differ in operation overloading ???
A const reference is simply that a reference that is const. It does not change the const-ness of the actual object you are passing in.
An example of non-const operator overloading:
Here is an example of operator overloading where the parameter is not const.
I DO NOT RECOMMEND TO DO THIS THOUGH:
class B
{
public:
const B& operator=(B& other)
{
other.x = 3;
x = other.x;
return *this;
}
int x;
};
void main(int argc, char** argv[])
{
B a;
a.x = 33;
B b;
b.x = 44;
a = b;//both a and b will be changed
return 0;
}
A const parameter is const throughout the function using it, it does not change its constness outside of it.
In this case you want to declare a const argument so that your assignment operator accepts both non-const variables and const variables; the latter case, in particular, includes the result of expressions, which is a temporary const variable and which you generally want to support in assignments.
If you used
CVector& CVector::operator= (CVector& param) // no const
then did this:
const CVector& my_vector = GetMyVector();
some_other_vector = my_vector; // call assignment operator - error!
You'll get an error because my_vector is a const CVector& and that can't be cast to a CVector& (non-const reference). It's just the local reference to it inside the operator= function that is const, not the entire object itself.
You can use the non-const variety, but this has two repercussions, one which is functional, and one which is about what you, as the writer of the function, are telling the user.
1) people calling the function that takes a non-const reference would not be able to call it using a const variable
2) when you have a function argument that's a non-const reference, you're signalling, "I reserver the right to change this". Typically, when a user of your function writes a = b;, he doesn't expect b to change.
Note that there's a third option you could use for this, pass-by-value:
CVector& CVector::operator= (CVector param) //no reference
This doesn't have either of the problems I mention above. However, it's very inefficient. Because of these three factors, passing by reference-to-const is preferred, especially in cases like a vector where copying can be expensive.
For the same reason you would use const anywhere: to ensure that future changes to the method don't inadvertently modify the passed in parameter, to help document the interface to notify callers that it is safe to pass param without risk of it changing, and to allow callers to pass in references that are declared as const in the calling code.
Another reason is to allow for conversions. For example:
string s = "foo";
s = "bar";
Here, an implementation might choose to only provide the assignment operator that takes a const reference to a string as a parameter, and depend on the compiler using a constructor to create a temporary string from the char * "bar". This would not work if the op='s parameter was not const, as you cannot bind a temporary to a non-const reference.
The const qualifier makes the passed parameter (in your example it is 'const CVector& param') as read only. The const qualifier ensures that the parameter (param) is not altered inside the operator=() method.
Without the const qualifier, the following is possible:
CVector& CVector::operator= (CVector& param)
{
x=param.x;
y=param.y;
param.x = 10; // some random value
param.y = 100;
return *this;
}
The above method alters the right hand side operand 'param' after assigning the value to the left hand side operand. The const qualifier helps you not to violate the semantics of the assignment operation.