Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

non-mutable lambdas don't show members as const #189

Closed
matkatmusic opened this issue Jun 7, 2019 · 6 comments
Closed

non-mutable lambdas don't show members as const #189

matkatmusic opened this issue Jun 7, 2019 · 6 comments

Comments

@matkatmusic
Copy link

#include <iostream>

struct Data
{
    int getData() const { return data; }
    void setData(int d) { data = d; }
private:
    int data {3};
};

int main()
{
    Data d;
    
    auto lambda = [=](int multiplier) /* mutable */-> int
    {
        //d.setData( d.getData() * multiplier );  //uncomment for errors
        return d.getData();
    };
    
    auto result = lambda( 4 );
    std::cout << "data's value: " << result << std::endl;
    
}

the generated lambda doesn't mark the private member in the anonymous body as const Data d;, which is should.

@matkatmusic
Copy link
Author

It generates this:

int main()
{
  Data d = Data();
    
  class __lambda_15_19
  {
    Data d;
    public: 
    inline /*constexpr */ int operator()(int multiplier) const
    {
      return d.getData();
    }
    
    public: __lambda_15_19(Data _d)
    : d{_d}
    {}
    
  };
  
  __lambda_15_19 lambda = __lambda_15_19{Data(d)};
  int result = lambda.operator()(4);
  std::operator<<(std::cout, "data's value: ").operator<<(result).operator<<(std::endl);
}

Shouldn't the anonymous lambda be defined like this instead?

  class __lambda_15_19
  {
    const Data d;
    public: __lambda_15_19(const Data _d)
    : d{_d}
    {}

@andreasfertig
Copy link
Owner

andreasfertig commented Jun 7, 2019

Hello @matkatmusic,

thank you for raising that point. First of all, I'm not a 100% sure about it myself. Here is my view.

The members don't need to be const as a member function can only call other const member functions. In addition, it cannot alter data of the class, regardless whether they are const or not. That all has nothing to do with lambdas but with classes in general.

Now for lambda have a look at a reduced and modified version of your code: https://godbolt.org/z/NfTBVM. The static_assert there triggers for both gcc and clang.

What does the Standard say? Nothing about that a member must be const. [expr.prim.lambda.capture] does talk about captures but I couldn't find a reference that they must be const. Look especially at §10 which specifically addresses capturing by copy.

All that said, I know that people often say/think that the members of a lambda are const in case of a non-mutable lambda. I also think I once saw a godbolt link proofing that, but I can't remember. Feel free to point me to a different direction.

Andreas

@matkatmusic
Copy link
Author

In addition, it cannot alter data of the class, regardless whether they are const or not. That all has nothing to do with lambdas but with classes in general.

Capture-by-reference can call non-const member functions.
capture-by-copy can only call const member functions unless you mark the lambda as mutable

you didn't share a godbolt link.

I'm going off of what is stated here: https://en.cppreference.com/w/cpp/language/lambda

Optional sequence of specifiers. The following specifiers are allowed:
    mutable: allows body to modify the parameters captured by copy, and to call their non-const member function

Is making an object const the only way to block the ability to call non-const member functions? Or is there some other way?

@matkatmusic
Copy link
Author

Ok, my fault. The standard says that the operator() is const by default:
http://eel.is/c%2B%2Bdraft/expr.prim.lambda.closure#4

@andreasfertig
Copy link
Owner

Capture-by-reference can call non-const member functions.

True and it can also modify capture by reference variables.

capture-by-copy can only call const member functions unless you mark the lambda as mutable

I don't think that is true. Your own example from above calls a const member function of Data in a non-mutable lambda. Am I missing something here?

you didn't share a godbolt link.

I updated my earlier comment with the godbolt link.

I'm going off of what is stated here: https://en.cppreference.com/w/cpp/language/lambda

Optional sequence of specifiers. The following specifiers are allowed:
    mutable: allows body to modify the parameters captured by copy, and to call their non-const member function

Is making an object const the only way to block the ability to call non-const member functions? Or is there some other way?

Yes, but that is true for all classes not just lambdas.

@matkatmusic
Copy link
Author

see my comment about the operator() being const.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants