Overloading with “const” at end of function declaration - c++

I have 3 attempts to overload function "begin()"
class Test
{
public:
//case 1: compiler error
int begin();
const int begin();
//case 2:compiler warning: type qualifiers ignored on function return type
int begin();
const int begin() const;
//case 3: ok
int begin();
int begin() const;
};
For the two cases that build successfully, how does the compiler choose which overload is called for begin()?

Member functions have an implicit argument which is the instance of the class itself. So you can think your functions as really looking like:
// 1) compile-error as const-qualifications on return doesn't distinguish
// the functions - you cannot overload on return type
int begin(Test& );
const int begin(Test& );
// 2)
int begin(Test& );
const int begin(const Test& );
// 3)
int begin(Test& );
int begin(const Test& );
With the 2nd and 3rd cases, the const-qualification on the function is equivalent to that implicit argument being a reference-to-const. So when you have something like:
Test{}.begin();
That calls begin() using a reference to non-const Test as the implicit first argument. Both overloads of begin() are viable, both require no conversions, so the least cv-qualified reference is preferred, which is the non-const-qualified function.
Instead, when you have:
(const Test{}).begin();
we're calling begin() with a reference to const Test. So the non-const-qualified function is not a viable candidate (you cannot pass a const-ref to a function expecting a non-const-ref), so the best viable candidate is the only viable candidate: int begin() const.

Case 1 is ambiguous cause return type doesn't participate in overload resolution,
case 2 is equivalent to case 3 because const is discarded in return type
In case 3:
const Test t1;
t1.begin() // calls int begin() const;
Test t2;
t2.begin() // calls int begin();
From cppreference.com:
A non-static member function can be declared with a const, volatile,
or const volatile qualifier (this qualifier appears after the name of
the function in function declaration). Differently cv-qualified
functions have different types and so may overload each other. In the
body of a cv-qualified function, the this pointer is cv-qualified,
e.g. in a const member function, only other const member functions may
be called normally. (A non-const member function may still be called
if const_cast is applied or through an access path that does not
involve this.)

A few rules to remember:
You can't overload methods based just on the return type. This leads to the error in your Case 1.
When returning by value, the const qualifier is redundant. In your case 2, compiler is warning you about this.
Every non-static method of the class has an implicit argument. On method invocation, this is assigned to the instance on which the method is invoked. The const keyword at end of method declaration(before the method body) applies to this implicit argument. What you are essentially saying is that the body of the function will not modify the state(data members) of the instance. If you call the method on a const instance, then you need to define the const method. Also, you can overload the method solely based upon the fact whether it is const or not.

Related

Two same overloaded operator [] one returning reference

Can I have two overloaded operator[] like this in the same class?
I am confused which definition is used when I use operator[], isn't int ambiguous? Don't they have the same signature?
template <class T, int n>
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
virtual T & operator[](int i);
virtual T operator[](int i) const;
};
This class contains declarations of these overloaded operator. I have not included definition in my question, though.
Overloaded operators works no different than normal overloaded functions. It's just they are special function. So I'm giving you general example from function same applies to any kind of functions.
As you must know that, top-level const has no effect on the
objects that can be passed to the function. A parameter that has a top-level const is
indistinguishable from one without a top-level const:
Record lookup(Phone);
Record lookup(const Phone); // redeclares Record lookup(Phone)
Record lookup(Phone*);
Record lookup(Phone* const); // redeclares Record lookup(Phone*)
In these declarations, the second declaration declares the same function as the first.
On the other hand, we can overload based on whether the parameter is a reference
(or pointer) to the const or nonconst version of a given type; such consts are
low-level.
// functions taking const and nonconst references or pointers have different parameters
// declarations for four independent, overloaded functions
Record lookup(Account&); // function that takes a reference to Account
Record lookup(const Account&); // new function that takes a constbreference.
Record lookup(Account*); // new function, takes a pointer to Account
Record lookup(const Account*); // new function, takes a pointer to const
In these cases, the compiler can use the constness of the argument to distinguish
which function to call. Because there is no conversion from const,
we can pass a const object (or a pointer to const) only to the version with a
const parameter. Because there is a conversion to const, we can call either
function on a nonconst object or a pointer to nonconst. However, the compiler will prefer the nonconst versions when we pass a
nonconst object or pointer to nonconst.
Examples from primer.

C++ Why the call is ambigous?

class myClass {
int arr[100];
public:
void *get(long i, void* const to) const;
void *get(long i, bool nog);
void *tstfn(void* const to) { return get(0L,to); }
};
gcc -Wall says:
dt.cpp: In member function ‘void* myClass::tstfn(void*)’:
dt.cpp:6:49: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
dt.cpp:4:9: note: candidate 1: void* myClass::get(long int, void*) const
dt.cpp:5:9: note: candidate 2: void* myClass::get(long int, bool)
Both function calls require a type conversion:
calling the void* function requires adding a const qualifer to this
calling the bool function requires converting to from void* to bool.
So, by the overload resolution rules, neither is a "better" match than the other, and the call is considered ambiguous.
Perhaps you can add const to the second function; perhaps you could remove it from the first (although I'd prefer not to); perhaps you can do an explicit type conversion of either this or to to force your preferred override.
Because void *get(long i, void* const to) is const.
This means that calling it from tstfn (which is non-const) would require qualification conversion for this from myClass* to const myClass*, so calling both functions would require a conversion for the arguments (this is treated in the same way as other arguments), so the call is ambiguous.
Simply because your testfn is a non-const function, which would call the non-const version of get. The non-const function get, takes bool not const void*. Had only one get function was there (possibly taking void* as the second argument, irrespective of its constness), then would be called without ambiguity.

C++ overload operator twice, one return non-const reference and the other const reference, what is the preference?

I overload an operator twice with the same parameter list. but with different return type:
T& operator()(par_list){blablabla}
const T& operator()(par_list){blablabla}
So when I'm calling the () operator, which function would be called based on what preference or situation? I know that if I call () under const function it has to be the const T& one.
I'm just curious how C++ deal with such situation and how the default preference works.
Thanks
These functions don't overload each other; they have the same signatures, and so the attempt to redefine the same function, which is an error. The return type is not part of a function's signature. To overload a function, you must declare a second function with the same name, but different parameters or const/volatile qualifiers - that is, qualifiers on the function, not the return type.
(They don't override each other either; overriding is what derived classes do to their base classes' virtual functions).
It's common to define a const and a non-const overload of a member function; the const overload must declare the function const, not just the return type:
T& operator()(par_list){blablabla}
const T& operator()(par_list) const {blablabla}
^^^^^
Now the first will be called if you apply () to a non-const object, and the second on a const object. For example:
Thingy nc;
Thingy const c;
nc(); // calls the first (non-const) overload
c(); // calls the second (const) overload
You can't overload a function/method based on return type. I would expect the compiler to throw an error here. What you can do is specify the method itself as a const method, using
const T& operator()(par_list) const {blahblah}
The const qualifier not only means this can be called on a const receiver, but it also is used in the overload resolution. This happens because it affects the implicit *this parameter that's passed to the method; a const method uses a const qualifier on *this, and const qualifiers are taken into account during overload resolution.
The way you define your operators, no way the compiler can decide which operator() to call. Overloading of functions (and operators) can only be done on the type of the arguments, never on the return type. And in fact, you will have an error at compilation as soon as you define the second one, the compiler considering that you are redefining the same function/operator.
However, the following is common (and probably what you have):
T& operator()(par_list){blablabla}
const T& operator()(par_list) const {blablabla}
This additional "const" after the argument list exists because you are defining non-static member functions and member functions have an implicit hidden argument: the "this" pointer to the instance of the class. The "const" keyword there indicates if this hidden pointer is to a const instance or not. This argument participates to the overloading resolution and that is in this case what the compiler use to choose which version of the operator to use.
So:
class A {
T& operator()() { ... }
const T& operator()() const { .... }
};
A a;
const A& ca(a);
a(); -> returns a T&
ca(); -> returns a const T&

A problem overloading [] in 2 variations

this is my generic class:
template<class T, class PrnT>
class PersonalVec {
public:
PersonalVec();
T &operator[](int index) const;
const T &operator[](int index) const;
private:
std::vector<T> _vec;
};
I'm required to implement 2 versions of [] operator:
one that will return a const reference and a regular one that will also return a reference.
When i compile it i get:
PersonalVec.hpp:23: error: ‘const T& PersonalVec<T, PrnT>::operator[](int) const’ cannot be overloaded
PersonalVec.hpp:22: error: with ‘T& PersonalVec<T, PrnT>::operator[](int) const
I've put either one of them as remark and then it does compile, so i guess they are colliding somehow. What is the problem and how can i fix it?
thank you!
You need:
T &operator[](int index);
const T &operator[](int index) const;
i.e. non-const operator returns non const reference and const one returns the const reference.
You can't overload based on return type, you can only overload based on parameter types, including the hidden this parameter for member functions.
The type of a function call expression, or an expression involving a potentially overloaded operator, is determined by the function type chosen by overload resolution, you cannot force such an expression to have a particular type and try to influence the overload resolution from the return type.
You need to either give your overloaded functions signatures that differ by parameter types or the constness of this, or you need to pick one appropriate return type and have a single function.
You will need to remove constness of the function when returning the non-const reference.
T &operator[](int index);
const T &operator[](int index) const;
Overloading does not and can not happen on the return type.
Return type of a function is not a criteria that can be used for overloading of functions.
A function can be overloaded if:
1. Different no of arguments
2. Differnt Sequence of arguments or
3. Different types of arguments
You are trying to overload the function based on return type and hence it gives the error.
The 'const' keyword can help you overload functions even if the above 3 criterias are not met. So simple solution can be to make one of the function const and keep other as a normal function

Functions with const arguments and Overloading

Was tryin out the stackeroverflow qn so it got me thinking why not overload the the function and I came up with a slightly different code but it says the function cannot be overloaded. My question is why? or is there a another way?
#include <iostream>
using std::cout;
class Test {
public:
Test(){ }
int foo (const int) const;
int foo (int );
};
int main ()
{
Test obj;
Test const obj1;
int variable=0;
do{
obj.foo(3); // Call the const function
obj.foo(variable); // Want to make it call the non const function
variable++;
usleep (2000000);
}while(1);
}
int Test::foo(int a)
{
cout<<"NON CONST"<<std::endl;
a++;
return a;
}
int Test::foo (const int a) const
{
cout<<"CONST"<<std::endl;
return a;
}
You can't overload based only on the constness of a non pointer, non reference type.
Think for instance if you were the compiler.
Faced with the line:
cout <<obj.foo(3);
which function would you call?
As you are passing by value the value gets copied either way. The const on the argument is only relevant to the function definition.
§13.1 where the Standard discusses about declarations that cannot be overloaded states -
Parameter declarations that differ
only in the presence or absence of
const and/or volatile are equivalent.
That is, the const and volatile
type-specifiers for each parameter
type are ignored [...]
Only the const and volatile
type-specifiers at the outermost level
of the parameter type specification
are ignored in this fashion; const and
volatile type-specifiers buried within
a parameter type specification are
significant and can be used to
distinguish overloaded function
declarations. [...]
when determining which function is
being declared, defined, or called.
"In particular, for any type T,
“pointer to T,” “pointer to const T,”
and “pointer to volatile T” are
considered distinct parameter types,
as are “reference to T,” “reference to
const T,” and “reference to volatile
T.”
EDIT 2:
As the post is essentially the same as the reffered post, except that the overloaded functions are now class member functions, I am trying to illustrate an additional aspect that could be useful to illustrate the concept of overloading which is not the same as overloading based on the 'constness' of the arguments (either in class scope or namespace scope). However the OP wanted to know how to differentiate the two overloads.
A way to overload them successfully relies on the cv qualification of the implied first parameter in case of member function calls as shown. The 'const' member function can only be called when the object expression used to invoke the overloaded member function is also a const. When a non const object expression is used to invoke the overloaded member function call, the non const version is preferred as it is an exact match (the call to const member function overload will require cv qualification of the first implied argument)
#include <iostream>
using std::cout;
class Test {
public:
Test(){}
int foo (const int) const;
int foo (int );
};
int main ()
{
Test obj;
Test const objc; // const object
obj.foo(3); // calls non const overload, object expression obj is non const
objc.foo(3); // calls const overload, object expression objc is const
}
int Test::foo(int a)
{
a++;
return a;
}
int Test::foo (const int a) const
{
return a;
}
As the answer to the other question explains, the two foos do not differ because they have equivalent parameter definitions.