Unexpected behaviour in C++ function overloading - c++

Consider the following code.
class MyClass1
{
public:
MyClass1(const int& i)
{
myVar = i;
}
const int& get() const
{
std::cout<<"Inside 1 \n";
return myVar;
}
int get()
{
std::cout<<"Inside 2 \n";
return myVar;
}
private:
int myVar;
};
class MyClass2
{
public:
MyClass2(const int& i)
{
myVar = i;
}
const int& get()
{
std::cout<<"Inside 3 \n";
return myVar;
}
int get() const
{
std::cout<<"Inside 4 \n";
return myVar;
}
private:
int myVar;
};
int main(int argc, char* argv[])
{
MyClass1 myClass1(10);
int tmp1 = myClass1.get();
const int& tmp2 = myClass1.get();
MyClass2 myClass2(10);
int tmp3 = myClass2.get();
const int& tmp4 = myClass2.get();
return 0;
}
The output showed the following.
Inside 2
Inside 2
Inside 3
Inside 3
For "const int& tmp2 = myClass1.get();" I expected that it will print call "const int& get() const" in MyClass1. To my surprise, it called "int get()" in both the cases in MyClass1.
In MyClass2, I swapped the "const" and then I found that in the function calls it called "const int& get()".
Why is it happening like this?

For "const int& tmp2 = myClass1.get();" I expected that it will print call "const int& get() const" in MyClass1.
You don't explain why you expect this, but you certainly shouldn't. There are two get functions that take no parameters, one const and the other not. Since myClass1 is not const, the function that is not const is called.
Were it not this way, there would be no point in allowing two class member functions that have the same name and take the same parameters to differ only in that one is const and the other isn't.

Related

What does const lambda mean?

#include <iostream>
int foo(int i)
{
const auto a = [&i](){ i = 7; return i * i; };
a();
return i;
}
int main()
{
std::cout << foo(42) << std::endl;
return 0;
}
This compiles( g++ -std=c++11 -Wall -Wextra -Wpedantic main.cpp ) and returns 49. Which is surprising to me, because by declaring a to be a constant object, I would have expected i to be referenced as const int&. It clearly isn't, why?
Lambdas are just like non-lambdas, except their implementation details are hidden. Therefore, it may be easier to explain using a non-lambda functor:
#include <iostream>
int foo(int i)
{
struct F {
int &i;
int operator()() const { i = 7; return i * i; }
};
const F a {i};
a();
return i;
}
int main()
{
std::cout << foo(42) << std::endl;
return 0;
}
F has a int & reference member i. const F cannot have its instance data modified, but a modification to i isn't a modification to its instance data. A modification to its instance data would be re-binding i to another object (which isn't allowed anyway).
[&i](){ i = 7; return i * i; }
is mainly equivalent to
class Lambda
{
public:
Lambda(int& arg_i) : i(arg_i) {}
auto operator() () const { i = 7; return i * i;}
private:
int& i;
};
And so then you have:
const Lambda a(i);
a();
And the const Lambda won't promote its member to const int& i; but int& const i; which is equivalent to int& i;.
When you capure i it is captured as the type it is.
So internally it has a int&. A const before the variable declaration of the closure does not change anything for the lambda.
You have 2 options to solve this:
const int i = 5;
auto b = [&i]() { i++; }; //error on i++
This way a const int& will be captured.
If you cannot change i for some reasons you can do this in c++14
int i = 5;
auto b = [i = static_cast<const int&>(i)]() { i++; }; //error on i++
This casts the int& to a const int& and will be stored as such in the lambda. Though this is way more verbose as you can see.
In the code you gave:
int foo(int i)
{
const auto a = [&i](){ i = 7; return i * i; };
a();
return i;
}
You are not assigning after you initialized your constant lambda function. Therefore, const doesn't mean much in this context.
What you have declared as const it isn't the context of your anonymous function or lambda exspression and its parameters, but only the reference at that lambda expression: const auto a.
Therefore, you cannot change the value of your lambda expr reference a because it is const, but its parameter passed by reference, &i, can be changed within the context of lambda expression.
If I understand correctly, the question is why you're allowed to mutate i even though a is const and presumably contains a reference to i as a member.
The answer is that it's for the same reason that you're allowed to do this on any object - assigning to i doesn't modify the lambda instance, it modifies an object it refers to.
Example:
class A
{
public:
A(int& y) : x(y) {}
void foo(int a) const { x = a; } // But it's const?!
private:
int& x;
};
int main()
{
int e = 0;
const A a(e);
a.foo(99);
std::cout << e << std::endl;
}
This compiles, and prints "99", because foo isn't modifying a member of a, it's modifying e.
(This is slightly confusing, but it helps to think about which objects are being modified and disregard how they're named.)
This "const, but not really" nature of const is a very common source of confusion and annoyance.
This is exactly how pointers behave, where it's more obviously not wrong:
class A
{
public:
A(int* y) : x(y) {}
void foo(int a) const { *x = a; } // Doesn't modify x, only *x (which isn't const).
private:
int* x;
};

C++ Force const-ness of lvalue in initializer expression

I would like the compiler to enforce const-ness of an lvalue (non-reference) but don't know if this is possible in C++. An example:
int foo() { return 5; }
int main() {
// Is there anything I can add to the declaration of foo()
// that would make the following cause a compile-error?
int a = foo();
// Whereas this compiles fine.
const int a = foo();
}
This is not really possible with something like an int because you need to give access to read the int and if they can read the int then they can copy it into a non-const int.
But from your comments it sounds like what you have in reality is not an int but a more complex user defined type, some sort of container perhaps. You can easily create an immutable container. This container could be a wrapper, or alternative implementation of your existing container. It then doesn't matter if the caller uses a const or non-const variable it is still immutable.
class MyClass {
std::vector<int> data;
public:
MyClass(size_t size) : data(size) {}
int& operator[](size_t index) { return data[index]; }
int operator[](size_t index) const { return data[index]; }
size_t size() const { return data.size(); }
};
class MyClassImmutable {
MyClass mc;
public:
MyClassImmutable(MyClass&& mc) : mc(std::move(mc)){}
int operator[](size_t index) const { return mc[index]; }
size_t size() const { return mc.size(); }
const MyClass& get() const { return mc; }
};
MyClassImmutable foo() {
MyClass mc(100);
mc[10] = 3;
return mc;
}
void func(const MyClass& mc);
int main() {
MyClassImmutable mci = foo();
std::cout << mci[10] << "\n"; // Can read individual values
//mci[10] = 4; // Error immutable
func(mc.get()); // call function taking a const MyClass&
}
Live demo.
Of course there is nothing to stop the caller from copying each and every value from your immutable container and inserting them into a mutable container.
Edit: An alternative approach might be to return a smart pointer-to-const. The only downside is you have to pay for a dynamic memory allocation:
std::unique_ptr<const MyClass> foo() {
auto mc = std::make_unique<MyClass>(100);
(*mc)[10] = 3;
return mc;
}
void func(const MyClass& mc);
int main() {
auto mc = foo();
std::cout << (*mc)[10] << "\n"; // Can read individual values
//(*mc)[10] = 4; // Error const
func(*mc); // can pass to a function taking a const MyClass&
}
It's not possible. foo() has no way of knowing about the type of the left hand side of the assignment, because when the assignment itself happens, foo() is already evaluated. The best you could hope for is to change the return value, to try and cause a type-based error on the initialization:
#include <type_traits>
struct my_int {
const int m;
template<typename T, typename std::enable_if<std::is_const<T>::value, T>::type* = nullptr>
constexpr operator T() const {return m;}
};
constexpr my_int foo() { return {5};}
int main() {
const int a = foo();
int b = foo();
}
Live example
But this will also not work, because the typename in the template will never be substitued by a const-qualified type (in this specific case, it will be int for both lines in main()).
As the following is possible
const int x = 4;
int y = x;
the C++ language will not provide such a mechanism.
Remains making a int const by a macro mechanism.
#define int_const_foo(var) const int var = ___foo()
int_const_foo(a);
Drawback: foo cannot be hidden, and the syntax is no longer C style.

function overload of a constant object

struct A {
void foo(int i, char& c) {
cout << "foo int char&" << endl;
}
void foo(int& i, int j) const {
cout << "const foo int& int" << endl;
}
};
int main() {
A a;
const A const_a;
int i = 1;
char c = 'a';
a.foo(i,i);
}
Will be printed:
const foo int& int
I dont understand why.
Why "const foo int& int" wont be printed?
I thought that constant Object can only call constant methods, and none const can call none const.
You misunderstood member-const.
A normal object can have any member function invoked on it, const or otherwise.
The constraint is that your const_a would not be able to have the non-const member function invoked on it. Unfortunately, you did not test that.

How does the compiler decide which function to call?

supposed there are two overloaded member function(a const version and a non-const version) in the String class:
char & String::operator[](int i) //Version 1
{
cout<<"char & String::operator[](int i) get invoked."<<std::endl;
return str[i];
}
const char & String::operator[](int i) const //Version 2
{
cout<<"const char & String::operator[](int i) const get invoked."<<std::endl;
return str[i];
}
and there is a test code fragment
int main(){
String a;
cout<<a[0]<<endl; //Line 1
a[0]='A'; //Line 2
}
How does the compiler decide which function to call? I found that Version 1 always got called when I run the program. Could anybody tell me why it is? And how can Version 2 got called?
If a is const, the second overload will get called.
int main(){
const String a;
cout<<a[0]<<endl; // would call const version
a[0]='A'; // will not compile anymore
}
If the object is const, the the const member function will be called. If the object is non-const the non-const member-function is called.
Exception
If their is only the const function, it is called in any case.
#include <iostream>
using namespace std;
class Foo {
public:
void print() {
cout << "Foo non-const member function\n";
}
void print() const {
cout << "Foo const member function\n";
}
};
class Bar {
public:
void print() const {
cout << "Bar const member function\n";
}
};
int main() {
Foo foo_non_const;
const Foo foo_const;
Bar bar_non_const;
const Bar bar_const;
foo_non_const.print();
foo_const.print();
bar_non_const.print();
bar_const.print();
return 0;
}
$ ./blah
Foo non-const member function
Foo const member function
Bar const member function
Bar const member function

How can I make this function act like an l-value?

Why can't I use the function ColPeekHeight() as an l-value?
class View
{
public:
int ColPeekHeight(){ return _colPeekFaceUpHeight; }
void ColPeekHeight( int i ) { _colPeekFaceUpHeight = i; }
private:
int _colPeekFaceUpHeight;
};
...
{
if( v.ColPeekHeight() > 0.04*_heightTable )
v.ColPeekHeight()-=peek;
}
The compiler complains at v.ColPeekHeight()-=peek. How can I make ColPeekHeight() an l-value?
Return the member variable by reference:
int& ColPeekHeight(){ return _colPeekFaceUpHeight; }
To make your class a good one, define a const version of the function:
const int& ColPeekHeight() const { return _colPeekFaceUpHeight; }
when I declare the function with the
two consts
When you want to pass an object into a function that you don't expect it to modify your object. Take this example:
struct myclass
{
int x;
int& return_x() { return x; }
const int& return_x() const { return x; }
};
void fun(const myclass& obj);
int main()
{
myclass o;
o.return_x() = 5;
fun(o);
}
void fun(const myclass& obj)
{
obj.return_x() = 5; // compile-error, a const object can't be modified
std::cout << obj.return_x(); // OK, No one is trying to modify obj
}
If you pass your objects to functions, then you might not want to change them actually all the time. So, to guard your self against this kind of change, you declare const version of your member functions. It doesn't have to be that every member function has two versions! It depends on the function it self, is it modifying function by nature :)
The first const says that the returned value is constant. The second const says that the member function return_x doesn't change the object(read only).
It can be rewritten like:
class View
{
public:
int GetColPeekHeight() const { return _colPeekFaceUpHeight; }
void SetColPeekHeight( int i ) { _colPeekFaceUpHeight = i; }
private:
int _colPeekFaceUpHeight;
};
...
{
cph = v.GetColPeekHeight();
if ( cph > 0.04 * _heightTable )
v.SetColPeekHeight( cph - peek );
}