Const method overloading - c++

I am wandering why C++ chooses to call non const methods on non const object for overloaded methods differing only by const method signature, Namely:
#include<iostream>
class Foo
{
public:
Foo() {}
int bar() const { std::cout<<"const version called"<<std::endl; }
int bar() { std::cout<<"version called"<<std::endl; }
};
int main()
{
Foo f;
f.bar();
const Foo g;
g.bar();
return 0;
}
I understand that for g object, being const, the const version of bar is called. But how about f? The output is
version called
const version called
Thanks for your insights.

This is how overload resolution works.
First, a list of candidate functions is constructed. In your case it consists of two functions:
int Foo::bar() const;
int Foo::bar();
Each function in the list is converted to accept this argument roughly like this:
int Foo_bar(const Foo* this);
int Foo_bar(Foo* this);
Then the choice is made according to the "Best viable function" (13.3.3) part of the C++ standard, which says, in short, that the best function is the one requiring the least number of conversions for its arguments. So if you have Foo f; and call f.bar(), the best function is the one accepting non-const this, because it requires no conversions, whereas the const version would require a qualification conversion for this from Foo* to const Foo*.

A non-const object can call both const as well as non-const versions of particular function. Whereas const object can only call const version of that function.

Related

C++ member functions with same name and parameters, different return type

is it valid if I define member functions with same name&parameters but different return types inside a class like this:
class Test {
public:
int a;
double b;
}
class Foo {
private:
Test t;
public:
inline Test &getTest() {
return t;
}
inline const Test &getTest() const {
return t;
}
}
Which member function gets called if we have following code?
Foo foo; // suppose foo has been initialized
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
no, it is not valid, but in your example it is, because the last const is actually part of the signature (the hidden Foo *this first parameter is now const Foo *this).
It is used to access in read-only (get const reference, the method is constant), or write (get non-const reference, the method is not constant)
it's still a good design choice to return the reference of the same entity (constant or non-constant) in both methods of course!
No.
You cannot overload on return type.
Why? The standard says so.
And it actually makes sense - you can't determine what function to call in all situations.
Const and non-const methods with the same formal parameter list can appear side-by-side because the this pointer is treated as a hidden argument and would have a different type. This may be used to provide mutating and non-mutating accessors as in the code in your question.
If the signatures are exactly the same, then no.
To expand upon the previous answers and your given code with an example so you can actually tell what's being called when:
#include <iostream>
class Test {
public:
int a;
double b;
};
class Foo {
private:
Test t;
public:
inline Test &getTest() {
std::cout << "Non const-refrence " << std::endl;
return t;
}
inline const Test &getTest() const {
std::cout << "Const-refrence " << std::endl;
return t;
}
};
int main() {
Foo foo;
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
const Foo bar;
const Test& t3 = bar.getTest();
return 0;
}
With output:
Non const-refrence
Non const-refrence
Const-refrence
The const you see after the second getTest signature tells the compiler that no member variables will be modified as a result of calling this function.
is it valid if I define member functions with same name&parameters but different return types [...]?
No.
Neither a method class nor a non-class function.
The reason is ambiguity. There would be situation in which the compiler could not pick the right overloading only by deducing the returned value.
In conclusion: you can't overload methods based on return type.
In your example, those two methods:
Test& getTest();
const Test& getTest() const;
Are correctly overloaded because the signature is different, but not because the return value is different!
Indeed, a function signature is made up of:
function name
cv-qualifiers
parameter types
method qualifier
So the signature of your methods are:
1) getTest();
2) getTest() const;
^------ Note that const qualifiers of the method is part of signature
As you can notice, the return value is not part of signature, but the const of the method qualifier is.
Which member function gets called if we have following code?
With the following code:
Foo foo;
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
It will call only the no-const method, even in the case t2.
The reason is that foo object is no-const in that scope, so each
method will be called in its no-const form.
In details, in the third line:
const Test& t2 = foo.getTest();
foo.getTest() will return the no-const reference and after will
be implicitly converted in a const reference.
If you want to force the compiler to call the const version, you should "temporary convert" the object foo in a const.
For example:
const int& t2 = static_cast<const Foo&>(foo).getTest();
In that case I get a const ref to the object, so the object will be treated like a const and the proper const method will be invoked.

When there is no non-const operator overload, will non-const object use const operator?

It seems it is in VS2013. But why in effective c++' item 3, the non-const operator calls const operator when they do exactly the same thing?
This is the code in Effective c++ item 3:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return const_cast<char&>( // cast away const on type;
static_cast<const TextBlock&>(*this)[position] // add const to *this's type;call const version of op[]
);
}
...
};
A const object cannot use a non-const method. (Remember that operators are only methods).
But the converse is true: A non-const object can use a const method (although it will use a non-const equivalent if that's present).
A method is only const if it is marked as const: a compiler is not allowed to assert const-ness by inspecting the function body.
Yes: if a const member function is present, but no non-const version, then calling that function on a non-const object will use the const function.
#include <iostream>
struct Foo {
void bar() const { std::cout << "const bar()\n"; }
};
int main() {
Foo f;
f.bar();
}
prints const bar().
Note the reverse is not true. You may not call a non-const member function on a const object:
struct Foo {
void bar() { std::cout << "non-const bar()\n"; }
};
int main() {
const Foo f;
f.bar();
}
gives a compiler error:
SO.cpp: In function 'int main()':
SO.cpp:9:11: error: passing 'const Foo' as 'this' argument of 'void Foo::bar()' discards qualifiers [-fpermissive]
f.bar();
^
EC++ Item 3, Use const whenever possible has a subsection, Avoiding Duplication in const and Non-const Member Functions. The reason it suggests that you don't just let the compiler pick the const version is that the return types are different (by a const) - the non-const version casts this away:
class CharWrapper {
char c_;
public:
char const& getC() const
{
std::cout << "const getC()\n";
return c_; // pretend this line is complicated
}
char& getC()
{
std::cout << "non-const getC()\n";
char const& c = const_cast<CharWrapper const&>(*this).getC(); // call the right one to avoid duplicating the work
return const_cast<char&>(c); // fix the result type
}
};
int main() {
CharWrapper wrapper;
wrapper.getC() = 'a';
}
Notice that it calls const operator and casts away constness (it one of those few times where it does not result in UB). It is needed so non-const operator returns a mutable reference. Otherwise you would not be able to modify objects contained in vector at all.
Call to existing operator instead of writing code again is done to avoid code duplication and possible future errors when you change one function, but forgot the other.
Regarding your comment: there is no move involved. It returns a reference. I would expect no difference in generated code for both const and non-const operator.

Why does non-const method hide const overload?

Given the code below:
class A
{
public:
A(): value( 0 ) {}
int* get()
{
return &value;
}
const int& get() const
{
return value;
}
private:
int value;
};
int main()
{
A a;
const int& ref_value = a.get();
}
results in the following compilation error:
prog.cpp: In function 'int main()':
prog.cpp:23:35: error: invalid conversion from 'int*' to 'int'
const int& ref_value = a.get();
^
It seems that the overloaded get() method with const modifier does get ignored completely and the compiler sees only the non-const definition of it. It is somehow understandable since the a object is not constant. One solution would be to make the a object constant. Though there are other two solutions that makes the code compileable:
Change the signature of the const get() method by different name or other parameters added.
int* get();
const int& get_changed() const; <-- this gets called
Change the non-const get() method to return a reference instead pointer.
int& get(); <-- this gets called
const int& get() const;
though with
int* get();
const int& get() const;
we have a compiler error.
What puzzles me is the reason behind all of these behavior.
When you have both a const and non-const overload of the same function with the same parameters, which one gets called depends only on the constness of the object on which you're invoking the function. So invoking on a non-const a must call the non-const overload.
It's exactly the same situation as this:
void foo(int *p);
void foo(const int *p);
int main()
{
int i;
const int ci;
foo(&i); // Calls the first overload
foo(&ci); // Calls the second overload
}
A const-qualified function can be called on a non-const-qualified object, but that requires a "nonconst to const" conversion. If there's an overload which doesn't require such a conversion (is a better match), it will be preferred.
The reason is that the compiler can't do overload resolution based on a different return type.
Only the function signature is taken into account for overload resolution. And the function signature consists only of the function name, the parameters, and the cv qualifiers - not the return type.

Overloading on l-values an

How can one invoke the second (overloaded) function?
(This example is present in Savitch's C++ textbook.)
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
I thought the first one is invoked in (a) and the second one in (b). But it is not.
(a) f() = 123; // the first one is invoked.
(b) f() + 3; // the first one is also invoked.
Only member functions can be const. So let's assume f actually is a member function in the textbook.
When the compiler has a choice between a const and a non const member function it will only use the const one if it has to. This is when the object the function is called on is const.
class A {
public:
int &f();
const int& f() const;
};
void func()
{
A a;
a.f(); // calls non const version
const A ca;
ca.f(); // call const version
}
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
These comments are wrong. If they appear in a textbook then I would recommend getting a different textbook.
Version 2 will be used when the expression denoting the object has const type, otherwise version 1 is used. It is nothing at all to do with lvalues and rvalues. Using Eelke's class definition:
A().f(); // invokes (1) on an rvalue
A const a;
a.f(); // invokes (2) on an lvalue
In your examples you don't actually show whether you are working on a const instance or not, but judging by your results, both must have been on a non-const one.
Overloading works on function parameters not by return type.
double f();
int f();
These are not overloading.
But these are:
double f();
int f(int);

It is possible to overload function by const specifier?

whether it is possible to overload function by const specifier. That is, we have two functions, one constant the other is not, can we say that the constant function overloaded non-const function ?
Yes and No.
It depends on where you put the const specifier.
When defining member functions, this is possible (Yes-part):
int f() { /*code*/ } //invoke this function on non-const object
int f() const { /*code*/ } //ok : invoke this function on const object
Note that in the absence of the first function, even non-const object will invoke the second function (i.e const member function) and in the absence of the second function, you wouldn't be able to invoke the first function on const objects!
But this is not possible (No-part):
int g() { /*code*/ }
const int g() { /*code*/ } //error: redefinition
irrespective of whether they're member functions or free functions.
Per ยง 13.1 / 2:
It's not possible to put const in return-type to overload:
Function declarations that differ only in the return type cannot be
overloaded.
int func();
const int func(); // Error
It's not possible to put const in parameter-list to overload:
Parameter declarations that differ only in the presence or absence of
const and/or volatile are equivalent.
void func(int x);
void func(const int x); // Error
BUT, it's possible:
const and volatile type-specifiers buried within a parameter type
specification are significant and can be used to distinguish
overloaded function declarations.
void func(int &x);
void func(const int &x); // OK
And, it's possible to put const at end of method declaration to distinguish overloads:
int func();
int func() const; // OK