What does const lambda mean? - c++

#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;
};

Related

Is this indirect const access an UB?

When I was checking some code today, I noticed an old method for implementing std::enable_shared_from_this by keeping a std::weak_ptr to self in the constructor. Somthing like this:
struct X {
static auto create() {
auto ret = std::shared_ptr<X>(new X);
ret->m_weak = ret;
return ret;
}
// use m_weak.lock() to access the object
//...
private:
X() {}
std::weak_ptr<X> m_weak;
};
But then something came to me regarding constness of this object. Check the following code:
struct X {
static auto create() {
auto ret = std::shared_ptr<X>(new X);
ret->m_weak = ret;
return ret;
}
void indirectUpdate() const {
m_weak.lock()->val = 1;
}
void print() const {
std::cout << val << '\n';
}
private:
X() {}
std::weak_ptr<X> m_weak;
int val = 0;
};
int main() {
auto x = X::create();
x->print();
x->indirectUpdate();
x->print();
}
In this code, indirectUpdate() is a const method and it should not update our object, but in fact it does. Because std::weak_ptr.lock() returns a non-const shared_ptr<> even though the method is const. So you will be able to update your object indirectly in a const method. This will not happen in case of std::enable_shared_from_this because shared_from_this returns a shared pointer to const ref of object in const method. I wonder if this code is UB or not. I feel it should be, but I'm not sure. Any idea?
Update:
Sorry, it seems that my question was not relayed correctly. I meant even if we have a const pointer, we lose that constness via this method. following code shows that:
struct X {
static auto create() {
auto ret = std::shared_ptr<X>(new X);
ret->m_weak = ret;
return ret;
}
void show() const { std::cout << "const \n";}
void show() { std::cout << "non-const\n";}
void indirectUpdate() const {
show();
m_weak.lock()->show();
m_weak.lock()->val = 1;
}
void print() const {
std::cout << val << '\n';
}
int val = 0;
private:
X() {}
std::weak_ptr<X> m_weak;
};
int main() {
// Here we have a const pointer
std::shared_ptr<const X> x = X::create();
x->print();
x->indirectUpdate();
x->print();
}
and output will be following:
0
const
non-const
1
which shows losing constness.
The object that is modified is not const. There is no undefined behavior.
Add a method like this:
#include <memory>
#include <iostream>
struct X {
static auto create() {
auto ret = std::shared_ptr<X>(new X);
ret->m_weak = ret;
return ret;
}
void show() const { std::cout << "const \n";}
void show() { std::cout << "non-const\n";}
void indirectUpdate() const {
m_weak.lock()->show();
m_weak.lock()->val = 1;
}
void print() const {
std::cout << val << '\n';
}
private:
X() {}
std::weak_ptr<X> m_weak;
int val = 0;
};
int main() {
auto x = X::create();
x->print();
x->indirectUpdate();
x->print();
}
To get this output:
0
non-const
1
Modifying an object via a const mehtod is ok, as long as the method only modifies an object that is actually not const.
It is similar to using a const & to a non-const object. You may cast away constness and modify it as long as the object is really not const:
#include <iostream>
int main() {
int x = 0;
const int& ref = x;
const_cast<int&>(ref) = 42;
std::cout << x;
}
I also see no danger of misusing the pattern in your code, because once the object is const you won't be able to assign to its val member and all is fine with constcorrectness.
In your Update you have a const pointer in main but the object is still not const. Consider this simpler example:
#include <iostream>
struct foo {
static foo* create(){
auto x = new foo();
x->self = x;
return x;
}
void bar() const {
this->self->non_const();
}
void non_const() {
std::cout << "Hello World\n";
}
foo* self;
};
int main() {
const foo* f = foo::create();
f->bar();
delete f;
}
Its not quite the same as yours, but it has similar effect of calling a non-const method on a seemingly const object. Though its all fine.
The only foo object is that created in foo::create, it is not constant. In main we have a const foo* to that object. main can only call the const member bar. Inside bar the this pointer is a const foo*, but self does not point to a const foo. self itself is `const, but not the object it points to.

How to watch instance method operator() result in VSCode C++?

My code is as follows:
class Foo{
public:
int operator()(int i)
{
return 1;
}
int operator++(int i)
{
return 1;
}
};
int main()
{
Foo foo;
int a = foo++;
int b = foo(0);
float c = 0;
}
The problem is that I am not able to watch foo(0):
foo++ is visible normally.
I think the reason is that overloaded function call operator becomes a FunctionObject type (see here https://en.cppreference.com/w/cpp/language/operators).
Is it possible to watch it?
As n-1-8e9-wheres-my-share-m commented, foo.operator()(0) works for me:

Unexpected behaviour in C++ function overloading

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.

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.

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 );
}