mercredi 15 septembre 2021

error: passing 'const S' as 'this' argument discards qualifiers

Here is an simplified version of the problem from ported from large code base. I've solved the issue, but I don't like the way I solved it.

The problematic code that doesn't compile is this I'm starting with:

#include <iostream>
#include <cstdlib>
#include <vector>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <array>
#include <utility>
#include <set>
#include <functional>

class S {
  public:
    int a;
    int b;
    mutable int c;
    void set_c() { c = 222; }
};

struct cmp
{
  bool operator()(const S& lhs, const S& rhs) const
  {
    return !(lhs.a == rhs.a && lhs.b == rhs.b);
  }
};

class core {
    public: 
      std::set<S, cmp> set_of_S;
      std::function<void()> f;
    
      void set_fn() {
        f = [this]() {
            auto it = set_of_S.begin();
            it->set_c();
        };
      }
};

int main()
{
    core core;
    
    S a {.a = 2, .b = 3, .c = 0};
    S b {.a = 2, .b = 3, .c = 0};
    S c {.a = 2, .b = 4, .c = 0};
    
    core.set_of_S.insert(a);
    core.set_of_S.insert(b);
    core.set_of_S.insert(c);

    core.set_fn();
    core.f();
    
    std::cout << core.set_of_S.size() << '\n';
}

The compiler error is:

prog.cc: In lambda function:
prog.cc:37:23: error: passing 'const S' as 'this' argument discards qualifiers [-fpermissive]
             it->set_c();

Ok, makes sense. As some people have told me, you should use the keyword mutable as this is not captured as a const and iterator it should be modifiable now (or atleast what I'm expecting):

      void set_fn() {
        f = [this]() mutable {
            auto it = set_of_S.begin();
            it->set_c();
        };
      } 

This doesn't compile. This part doesn't make sense to me. So a member function cannot modify captured this inside lambda, but if you try to directly modify S::c inside the lambda compiler thinks that is okay. What? Doesn't make sense to me.

When I change:

void set_c() { c = 222; }

to

void set_c() const { c = 222; }

It will finally compile, but I don't like the solution, because we had to modify the original function signature just because the lambda won't accept it and it makes it less readable. I see lambdas as a tool and not something you have to design against. I have tried placing mutable keyword all over the place, but can't get it to compile. And I think there should be a way to permit member function to modify it's own state inside lambda.

Am I missing something or is this a compiler bug?

Here is the problematic code in wandbox: https://wandbox.org/permlink/qzFMW6WIRiKyY3Dj

I know this has been asked in: error: passing xxx as 'this' argument of xxx discards qualifiers but answers won't discuss on using mutable which to my understanding should solve these kind of situations.

Aucun commentaire:

Enregistrer un commentaire