why must you provide the keyword const in operator overloads - c++

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.

Related

Is it possible to do (member) function overloading based on if the caller is an rvalue or lvalue? [duplicate]

C++11 makes it possible to overload member functions based on reference qualifiers:
class Foo {
public:
void f() &; // for when *this is an lvalue
void f() &&; // for when *this is an rvalue
};
Foo obj;
obj.f(); // calls lvalue overload
std::move(obj).f(); // calls rvalue overload
I understand how this works, but what is a use case for it?
I see that N2819 proposed limiting most assignment operators in the standard library to lvalue targets (i.e., adding "&" reference qualifiers to assignment operators), but this was rejected. So that was a potential use case where the committee decided not to go with it. So, again, what is a reasonable use case?
In a class that provides reference-getters, ref-qualifier overloading can activate move semantics when extracting from an rvalue. E.g.:
class some_class {
huge_heavy_class hhc;
public:
huge_heavy_class& get() & {
return hhc;
}
huge_heavy_class const& get() const& {
return hhc;
}
huge_heavy_class&& get() && {
return std::move(hhc);
}
};
some_class factory();
auto hhc = factory().get();
This does seem like a lot of effort to invest only to have the shorter syntax
auto hhc = factory().get();
have the same effect as
auto hhc = std::move(factory().get());
EDIT: I found the original proposal paper, it provides three motivating examples:
Constraining operator = to lvalues (TemplateRex's answer)
Enabling move for members (basically this answer)
Constraining operator & to lvalues. I suppose this is sensible to ensure that the "pointee" is more likely to be alive when the "pointer" is eventually dereferenced:
struct S {
T operator &() &;
};
int main() {
S foo;
auto p1 = &foo; // Ok
auto p2 = &S(); // Error
}
Can't say I've ever personally used an operator& overload.
One use case is to prohibit assignment to temporaries
// can only be used with lvalues
T& operator*=(T const& other) & { /* ... */ return *this; }
// not possible to do (a * b) = c;
T operator*(T const& lhs, T const& rhs) { return lhs *= rhs; }
whereas not using the reference qualifier would leave you the choice between two bads
T operator*(T const& lhs, T const& rhs); // can be used on rvalues
const T operator*(T const& lhs, T const& rhs); // inhibits move semantics
The first choice allows move semantics, but acts differently on user-defined types than on builtins (doesn't do as the ints do). The second choice would stop the assigment but eliminate move semantics (possible performance hit for e.g. matrix multiplication).
The links by #dyp in the comments also provide an extended discussion on using the other (&&) overload, which can be useful if you want to assign to (either lvalue or rvalue) references.
If f() needs a Foo temp that is a copy of this and modified, you can modify the temp this instead while you can't otherwise
On the one hand you can use them to prevent functions that are semantically nonsensical to call on temporaries from being called, such as operator= or functions that mutate internal state and return void, by adding & as a reference qualifier.
On the other hand you can use it for optimizations such as moving a member out of the object as a return value when you have an rvalue reference, for example a function getName could return either a std::string const& or std::string&& depending on the reference qualifier.
Another use case might be operators and functions that return a reference to the original object such as Foo& operator+=(Foo&) which could be specialized to return an rvalue reference instead, making the result movable, which would again be an optimization.
TL;DR: Use it to prevent incorrect usage of a function or for optimization.

What's a use case for overloading member functions on reference qualifiers?

C++11 makes it possible to overload member functions based on reference qualifiers:
class Foo {
public:
void f() &; // for when *this is an lvalue
void f() &&; // for when *this is an rvalue
};
Foo obj;
obj.f(); // calls lvalue overload
std::move(obj).f(); // calls rvalue overload
I understand how this works, but what is a use case for it?
I see that N2819 proposed limiting most assignment operators in the standard library to lvalue targets (i.e., adding "&" reference qualifiers to assignment operators), but this was rejected. So that was a potential use case where the committee decided not to go with it. So, again, what is a reasonable use case?
In a class that provides reference-getters, ref-qualifier overloading can activate move semantics when extracting from an rvalue. E.g.:
class some_class {
huge_heavy_class hhc;
public:
huge_heavy_class& get() & {
return hhc;
}
huge_heavy_class const& get() const& {
return hhc;
}
huge_heavy_class&& get() && {
return std::move(hhc);
}
};
some_class factory();
auto hhc = factory().get();
This does seem like a lot of effort to invest only to have the shorter syntax
auto hhc = factory().get();
have the same effect as
auto hhc = std::move(factory().get());
EDIT: I found the original proposal paper, it provides three motivating examples:
Constraining operator = to lvalues (TemplateRex's answer)
Enabling move for members (basically this answer)
Constraining operator & to lvalues. I suppose this is sensible to ensure that the "pointee" is more likely to be alive when the "pointer" is eventually dereferenced:
struct S {
T operator &() &;
};
int main() {
S foo;
auto p1 = &foo; // Ok
auto p2 = &S(); // Error
}
Can't say I've ever personally used an operator& overload.
One use case is to prohibit assignment to temporaries
// can only be used with lvalues
T& operator*=(T const& other) & { /* ... */ return *this; }
// not possible to do (a * b) = c;
T operator*(T const& lhs, T const& rhs) { return lhs *= rhs; }
whereas not using the reference qualifier would leave you the choice between two bads
T operator*(T const& lhs, T const& rhs); // can be used on rvalues
const T operator*(T const& lhs, T const& rhs); // inhibits move semantics
The first choice allows move semantics, but acts differently on user-defined types than on builtins (doesn't do as the ints do). The second choice would stop the assigment but eliminate move semantics (possible performance hit for e.g. matrix multiplication).
The links by #dyp in the comments also provide an extended discussion on using the other (&&) overload, which can be useful if you want to assign to (either lvalue or rvalue) references.
If f() needs a Foo temp that is a copy of this and modified, you can modify the temp this instead while you can't otherwise
On the one hand you can use them to prevent functions that are semantically nonsensical to call on temporaries from being called, such as operator= or functions that mutate internal state and return void, by adding & as a reference qualifier.
On the other hand you can use it for optimizations such as moving a member out of the object as a return value when you have an rvalue reference, for example a function getName could return either a std::string const& or std::string&& depending on the reference qualifier.
Another use case might be operators and functions that return a reference to the original object such as Foo& operator+=(Foo&) which could be specialized to return an rvalue reference instead, making the result movable, which would again be an optimization.
TL;DR: Use it to prevent incorrect usage of a function or for optimization.

"Reference qualifier correctness" or should a non-const method ever apply to rvalues?

Now that GCC 4.8.1 and Clang 2.9 and higher support them, reference qualifiers (also known as "rvalue references for *this") have become more widely available. They allow classes to behave even more like built-in types by, e.g., disallowing assignment to rvalues (which can otherwise cause an unwanted cast of an rvalue to an lvalue):
class A
{
// ...
public:
A& operator=(A const& o) &
{
// ...
return *this;
}
};
In general, it is sensible to call a const member function of an rvalue, so an lvalue reference qualifier would be out of place (unless the rvalue qualifier can be used for an optimization such as moving a member out of a class instead of returning a copy).
On the flip side, mutating operators such as the pre decrement/increment operators should be lvalue-qualified, as they usually return an lvalue-reference to the object. Hence also the question: Are there any reasons to ever allow mutating/non-const methods (including operators) to be called on rvalue references aside from conceptually const methods which are only not marked const because const-correctness (including proper application of mutable when using an internal cache, which may include ensuring certain thread-saftey guarantees now) was neglected in the code base?
To clarify, I am not suggesting to forbid mutating methods on rvalues on the language level (at the very least this could break legacy code) but I believe that defaulting (as an idiom / coding style) to only allowing lvalues for mutating methods will generally lead to cleaner, safer APIs. However I am interested in examples where not doing so leads to cleaner, less astonishing APIs.
A mutator that operates on an R-value can be useful if the R-value is used to accomplish some task, but in the interim it maintains some state. For example:
struct StringFormatter {
StringFormatter &addString(string const &) &;
StringFormatter &&addString(string const &) &&;
StringFormatter &addNumber(int) &;
StringFormatter &&addNumber(int) &&;
string finish() &;
string finish() &&;
};
int main() {
string message = StringFormatter()
.addString("The answer is: ")
.addNumber(42)
.finish();
cout << message << endl;
}
By allowing either an L-value or an R-value, one can construct an object, pass it through some mutators, and use the result of the expression to accomplish some task without having to store it in an L-value, even if the mutators are member functions.
Also note that not all mutating operators return a reference to the self. User-defined mutators can implement any signature they need or want. A mutator may consume the state of the object to return something more useful, and by acting on an R-value, the fact that the object is consumed isn't a problem since the state would have otherwise been discarded. In fact, a member function that consumes the state of the object to produce something else useful will have to be marked as such, making it easier to see when l-values are consumed. For example:
MagicBuilder mbuilder("foo", "bar");
// Shouldn't compile (because it silently consumes mbuilder's state):
// MagicThing thing = mbuilder.construct();
// Good (the consumption of mbuilder is explicit):
MagicThing thing = move(mbuilder).construct();
I think it comes about in cases where the only way to retrieve some value is by mutating another value. For instance, iterators don't provide a "+1" or a "next" method. So suppose I'm constructing a wrapper for stl list iterators (perhaps to create an iterator for my own list-backed data-structure):
class my_iter{
private:
std::list::iterator<T> iter;
void assign_to_next(std::list::iterator<T>&& rhs) {
iter = std::move(++rhs);
}
};
Here, the assign_to_next method takes an iterator and assigns this one to have the next position after that one. It's not too hard to imagine situations where this might be useful, but more importantly there is nothing surprising about this implementation. True, we could also say iter = std::move(rhs); ++iter; or ++(iter = std::move(rhs));, but I don't see any arguments for why those would be any cleaner or faster. I think this implementation is the most natural to me.
FWIW HIC++ agrees with you as far as assignment operators:
http://www.codingstandard.com/rule/12-5-7-declare-assignment-operators-with-the-ref-qualifier/
Should a non-const method ever apply to rvalues?
This question puzzles me. A more sensible question to me would be:
Should a const method ever apply exclusively to rvalues?
To which I believe the answer is no. I can't imagine a situation in which you would want to overload on const rvalue *this, just as I can't imagine a situation in which you would want to overload on const rvalue arguments.
You overload on rvalues because it's possible to handle them more efficiently when you know that you can steal their guts, but you can't steal the guts of a const object.
There are four possible ways to overload on *this:
struct foo {
void bar() &;
void bar() &&;
void bar() const &;
void bar() const &&;
};
The constness of the latter two overloads means that neither one can mutate *this, so there can be no difference between what the const & overload is allowed to do to *this and what the const && overload is allowed to do to *this. In the absence of the const && overload, the const & will bind to both lvalues and rvalues anyway.
Given that overloading on const && is useless and only really provided for completeness (prove me wrong!) we are left with only one remaining use case for ref-qualifiers: overloading on non-const rvalue *this. One can define a function body for a && overload, or one can = delete it (this happens implicitly if only a & overload is provided). I can imagine plenty of cases in which defining a && function body might be useful.
A proxy object which implements pointer semantics by overloading operator-> and unary operator*, such as boost::detail::operator_arrow_dispatch, might find it useful to use ref-qualifiers on its operator*:
template <typename T>
struct proxy {
proxy(T obj) : m_obj(std::move(obj)) {}
T const* operator->() const { return &m_obj; }
T operator*() const& { return m_obj; }
T operator*() && { return std::move(m_obj); }
private:
T m_obj;
};
If *this is a rvalue then operator* can return by move instead of by copy.
I can imagine functions that move from the actual object to a parameter.

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 non-const version is selected over the const version for class?

Following is the test code:
struct A
{
operator int ();
operator int () const;
};
void foo (const int);
Now, upon invoking:
foo(A()); // calls A::operator int()
Why does it always chooses the non-const version ? Even making operator const int () const; doesn't have any effect on invoking foo(). Apart from standard reference, can someone explain logically, the reason behind it ?
A() gives you a temporary A object that is not const-qualified. The A() expression is an rvalue expression, yes, but that does not make the A object const-qualified.
Since the A object is not const-qualified, the non-const operator int() is an exact match and the const operator int() requires a qualification conversion, so the non-const overload is selected as the best match.
If you want it to be const-qualified, you'd need to explicitly request a const-qualified A:
foo(identity<const A>::type());
where identity is defined as
template <typename T>
struct identity { typedef T type; };
Note that there is really no difference between operator const int() const and operator int() const: the result is an rvalue and only class-type rvalues can be const-qualified (int is not a class type).
Note also that there is no difference between the void foo(const int) that you have and void foo(int). Top-level const-qualifiers on parameter types do not affect the type of the function (i.e., the type of both of those declarations is void foo(int)). Among other reasons, this is because it doesn't matter to the caller whether there is a top-level const-qualifier; it has to make a copy regardless. The top-level const-qualifier affects only the definition of the function.
James McNellis’ answer really covered it all, but it doesn’t hurt (I hope) with more explanations.
So.
When you call …
o.operator int()
… then the overload selection depends entirely on the constness of o.
Nothing else.
To see why, consider this class:
struct Bar
{
void f() {}
void f() const {}
};
Technically those member functions do not need to be member functions. They could just as well have been chosen to be free standing functions. But then they need Bar argument:
struct Bar
{};
void f( Bar& ) {}
void f( Bar const& ) {}
And hopefully now it's easier to see that when you do
Bar o;
f( o );
then the first function can be selected. And so it is. Because if the second function was selected, then you could never get the first one. Because if you make the object const, then it would break const correctness to select the first one. So when the object is const only the second can be selected, hence, when it is not const the first one is selected.
In short, the only practical alternative to this rule would be to always select the second one, which would make the first one rather useless, yes?
Cheers & hth.,
One rule you have to remember about C++: it never takes into account the value that is being returned when it selects an overload. In this case since the operator int function takes no parameters, it can't use the parameter list to narrow down the choices either. All it can use it the constness of the object that it's being called from. Since this is a new temporary object, it's not const, so it doesn't choose the const overload.