Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copied const object in lambda closure not mutable

Tags:

c++

c++11

lambda

I'm trying to capture a const object via copy in a (mutable) lambda. My compiler however complains, that the captured object is const.

Should it not be possible to copy the object as non-const?

struct Foo
{
    Foo(){}
    void Func(){}
};

int main()
{
    const Foo foo;
    [foo]() mutable { foo.Func(); };
}

Compiling with g++ 4.7.2:

testcase.cpp: In lambda function:
testcase.cpp:10:29: error: no matching function for call to ‘Foo::Func() const’
testcase.cpp:10:29: note: candidate is:
testcase.cpp:4:7: note: void Foo::Func() <near match>
testcase.cpp:4:7: note:   no known conversion for implicit ‘this’ parameter from ‘const Foo*’ to ‘Foo*’

Compiling with clang++ 3.1:

testcase.cpp:10:20: error: member function 'Func' not viable: 'this' argument has type 'const Foo', but function is not marked const
    std::async([foo]() mutable { foo.Func(); });

The Standard document (or rather the draft...) defines in 5.1.2.14 that "The type [...] is the type of the corresponding captured entity", so I guess that would include the cv-specifiers.
It does not seem intuitive though.

like image 583
z33ky Avatar asked Dec 04 '12 09:12

z33ky


2 Answers

First, the type of a lambda expression, which has capture, is a class type (5.1.2 Lambda expressions [expr.prim.lambda] #3)

That type has an operator() which is by default const, unless mutable is used in the lambda expression ([expr.prim.lambda] #5)

Next, for each entity captured as copy, a unnamed member is declared in the closure type. [expr.prim.lambda] #14]

If you explicitly build the (mostly) equivalent of the capture type, everything will naturally follow from the usual semantics for classes, const-qualified types and const qualified member functions.

Example:

struct S
{
  void f();
  void fc() const;
};

void g()
{
  S s0;

  // [s0] ()  { s0.f(); }; // error, operator() is const
  [s0] () { s0.fc(); };    // OK, operator() is const, S::fc is const

  [s0] () mutable { s0.f(); };
  [s0] () mutable { s0.fc(); };

  const S s1;

  // [s1] ()  { s1.f(); }; // error, s1 is const, no matter if operator() is const
  [s1] ()  { s1.fc(); };

  // [s1] () mutable { s1.f(); }; // error, s1 is const, no matter if operator() is const
  [s1] () mutable { s1.fc(); };
}

I guess the confusion stems from the fact that mutable in the lambda-declarator concerns the const-ness of the operator(), not the mutable-ility of the data members of the closure type. It would be more natural to use const, as with member functions, but I guess the standards committee wanted const to be the default.

like image 165
chill Avatar answered Nov 02 '22 03:11

chill


This will work under C++14, not a solution for C++11.

struct Foo
{
    Foo(){}
    void Func(){}
};

int main()
{
    const Foo foo;
    [foo = foo]() mutable { foo.Func(); };
}

But I don't really understand why [foo] keeps the const but [foo = foo] not.

like image 43
任长风 Avatar answered Nov 02 '22 01:11

任长风