This question already has answers here:
C++0x lambda capture by value always const?
(2 answers)
Closed 1 year ago.
class TestGetStr {
public:
string a;
string getString() {
return a;
}
};
void acceptConstString(const string & a) {
std::cout << a << std::endl;
}
int main(int argc, const char * argv[]) {
auto testGetStr = TestGetStr();
acceptConstString(testGetStr.getString()); //compile ok
auto testaaa = [testGetStr](){
//compile error 'this' argument to member function 'getString' has type 'const TestGetStr', but function is not marked const
acceptConstString(testGetStr.getString());
};
Difference between normal call and lambda capture?
The normal call can compile, but the lambda call can't compile.
And Why? Thanks for more detail.
You can add mutable qualifier.
auto testaaa = [testGetStr]() mutable {
// ^^^^^^^
acceptConstString(testGetStr.getString());
};
Otherwise the operator() of the lambda is const-qualified, and then can't call non-const member functions on the captured object.
mutable: allows body to modify the objects captured by copy, and to call their non-const member functions
Unless the keyword mutable was used in the lambda-expression, the
function-call operator is const-qualified and the objects that were
captured by copy are non-modifiable from inside this operator().
PS: Or better to make TestGetStr::getString() const member function.
Related
This question already has answers here:
Pointer to member function - syntax
(2 answers)
Closed last year.
class C{
public:
int i(){return 3;}
};
void memfn(int (C::* const & func)()){}
void fn(int (* const & func)()){}
int main() {
fn(&foo); //Works
fn(foo); //Works
memfn(&C::i); //Works
memfn(C::i); //Doesn't Work
}
When passing a function pointer as a parameter, the address-of operator is optional on the function. Why does it not work if removed on a member function?
There is an implicit conversion from global function references, or static member function references, or non-capturing lambdas, to function pointers. This is for capability with C.
But such implicit reference-to-pointer conversions are not for non-static member functions (and capturing lambdas!), because they need an implicitly passed this pointer to work, the conversion just lacks that thing.
In your case, if we make i() static, then it can be passed to fn either by reference or by address.
This question already has answers here:
Why can a const method take a non const reference?
(2 answers)
Closed 2 years ago.
Consider the following code:
#include <iostream>
class ObjectCalculator {
public:
struct Object {
int id = 0;
};
void setObject(Object& object) const {
object.id = 1;
}
Object m_object;
};
int main() {
ObjectCalculator objCalc{};
std::cout << objCalc.m_object.id << std::endl;
objCalc.setObject(objCalc.m_object);
std::cout << objCalc.m_object.id << std::endl;
return 0;
}
I thought this this line should throw an error during compilation since the internal variable is changed via const method setObject:
objCalc.setObject(objCalc.m_object);
but the code compiles with no errors and outputs:
0
1
Could you please clarify why const does not have effect in this case?
const related to the members of the class.
You modifying the method argument, so it can be changed, regardless of const.
const after method applied to the implicit first method argument this, through which all class members accessed.
cppreference
A non-static member function can be declared with a const, volatile,
or const volatile qualifier (this qualifier appears after the
parameter list in the 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.)
The const qualifier is to tell that the function does not modify this object.
By passing the object to modify as an argument you don't modify this, you modify the object passed as an argument.
If you tried to modify m_object in the function, you would get the error you expected.
This question already has answers here:
Returning non-const reference from a const member function
(3 answers)
Closed 7 years ago.
I have a class with a list as member, and a getter on this list:
class A
{
public:
const std::list<int*>& get() const {
return list;
}
private:
std::list<int*> list;
};
Why the get function has to return a const std::list and not just an std::list ?
Otherwise I get this error :
error: invalid initialization of reference of type 'std::list&'
from expression of type 'const std::list'
The problem is that the function get is declared as const:
const std::list<int*>& get() const
// ^
, so list is considered as a const inside the function.
Hence the attempt to return it as a non-const reference fails.
Problem
The issue here is that get is const qualified, therefore it can only return a const reference to one of its member objects. If you want to return a non-const reference then you have to make get non const-qualified.
If you think about this also makes sense from the point of view of const-correctness: if you are allowed to return a non-const reference to a member object from a const qualified member function, then you can effectively modify a constant object via that reference, for example via:
A const a(...);
std::list<int*> ref = a.get(); // const qualified but returning non const reference
ref.clear(); // wiped out contents of list in `a`
Possible solutions
I'd recommend to provide both overloads as follows:
const std::list<int*>& get() const { return list; }
std::list<int*>& get() { return list; }
At this point it might be worth considering to just make the list public, depending on the context:
struct A {
std::list<int*> list;
};
Consider the following:
void test( const int &value )
{
auto testConstRefMutableCopy = [value] () mutable {
value = 2; // compile error: Cannot assign to a variable captured by copy in a non-mutable lambda
};
int valueCopy = value;
auto testCopyMutableCopy = [valueCopy] () mutable {
valueCopy = 2; // compiles OK
};
}
Why is the first version a compile error when I've declared the lambda as mutable and captured value by value (which I thought made a copy of it)?
Tested with clang (x86_64-apple-darwin14.3.0), which is where the error message comes from, and Visual C++ (vc120).
[C++11: 5.1.2/14]: An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise. [..]
The type of value inside your lambda is const int, because it was captured by copy from a const int&.
Thus, even though the lambda's call operator function is not const (you marked the lambda mutable), the actual implicit member value is of type const int and cannot be mutated.
Frankly, this seems absurd; I would expect this rule to say that the referenced type loses constness, as it's a copy. The presence or absence of the mutable keyword on the lambda itself (and, thus, the presence or absence of the const keyword on the generated call operator function) should be the only access control here.
In C++14 you can work around this by capturing as [value=value], which uses the same rules as auto and thus drops the const. C++'s great, ain't it?
mutable allows a lambda to modify copy of a non-const parameter captured by copy, but it does not allow it for const parameters.
So this code works (and outputs inside 2 outside 1):
int a = 1;
[a]() mutable {
a = 2; // compiles OK
cout << "inside " << a << "\n";
}();
cout << " outside " << a << "\n";
But if we omit mutable, or make a const int, the compiler gives an error.
In our case, the first lambda gives an error because value is const:
void test( const int &value )
If we make copyValue const:
const int valueCopy = value;
then the same error will occur with the second lambda.
I have a function that uses a lambda expression.
std::vector<Bar*> mBars;
void foo(Bar* bar)
{
auto duplicateBars = std::remove_if(mBars.begin(), mBars.end(),
[bar] (const Bar* const &element)
{
return bar == element;
});
mBars.erase(duplicateBars, mBars.end());
}
Later, I reviewed the code and realized I could add two consts to foo's signature.
void foo(const Bar* const bar);
bar's pointer and data is now constant, but for the purpose of the lambda expression the pointer itself is constant, because I captured by value. However, the data pointed to can be changed and there is no way to change this, because const is not allowed in the lambda capture.
This is unintuitive to me. Is my interpretation correct? I can use the second signature, but I cannot protect the data from being changed in the lambda expression.
However, the data pointed to can be changed and there is no way to change this, because const is not allowed in the lambda capture.
No, when capturing by value in a lambda expression constness is preserved, i.e. capturing a pointer to const data will prevent changes to the data inside the lambda.
int i = 1;
const int* ptr = &i;
auto func = [ptr] {
++*ptr; // ERROR, ptr is pointer to const data.
}
A lambda will also add top-level constness to pointers when capturing by value (unless using mutable).
auto func = [ptr] {
ptr = nullptr; // ERROR, ptr is const pointer (const int* const).
}
auto func = [ptr] () mutable { // Mutable, will not add top-level const.
ptr = nullptr; // OK
}
I can use the second signature, but I cannot protect the data from being changed in the lambda expression.
You can protect the data from being changed inside the lambda by using const.
const Bar* bar = &bar_data;
auto b = [bar] (const Bar* element) { // Data pointed to by bar is read-only.
return bar == element;
};
Also the lambda expression takes a parameter of type const Bar* const &, i.e. reference to const pointer to const data. No need to take a reference, simply take a const Bar*.
More info about pointers and const: What is the difference between const int*, const int * const, and int const *?
Your question seems to arise from a misunderstanding of how capturing of variables in lambda expressions works. When you capture a variable by copy, the corresponding data member created in the closure type generated from the lambda expression will have the same type as the original object. This preserves const-ness, and you cannot go modify whatever bar points to within the body of the lambda.
From ยง5.1.2/15 [expr.prim.lambda]
An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that is not of the form & identifier or & identifier initializer. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding
captured entity if the entity is not a reference to an object, or the referenced type otherwise.
Live demo