Milan
Milan

Reputation: 2017

Getting passing ‘const std::map<int, bool>’ as ‘this’ argument discards qualifiers [-fpermissive] in lambda function

Here is a basic code snippet for which I'm getting an error:

error: passing ‘const std::map<int, bool>’ as ‘this’ argument discards qualifiers [-fpermissive]`

struct Point {
    float x;
    float y;
    int id;
    Point(float x, float y, float id) : x(x), y(y), id(id) {}
};

void removePoints(std::vector<Point> &points_vec) {
    std::map<int, bool> my_map;
    for (const auto& pt : points_vec) {
        if(pt.id < 0) 
            my_map[pt.id] = true;
        else
            my_map[pt.id] = false;
    }

    points_vec.erase(std::remove_if(points_vec.begin(), points_vec.end(), [map_lambda = my_map] (const Point pt) -> bool {
        return map_lambda[pt.id];
    }), points_vec.end());
}

int main(int argc, char const *argv[]) {
    std::vector<Point> points_vec;
    points_vec.push_back(Point(1, 2, 0));
    points_vec.push_back(Point(1, 5, -1));
    points_vec.push_back(Point(3, 3, -1));
    points_vec.push_back(Point(4, 9, 2));
    points_vec.push_back(Point(0, 1, 3));
    points_vec.push_back(Point(-1, 7, -2));

    std::cout << points_vec.size() << std::endl;
    removePoints(points_vec);
    std::cout << points_vec.size() << std::endl;
    
    return 0;
}

Note: I know I can remove points without using std::map, but the above code snippet is just an example of a bigger problem.

I checked some questions on a similar error:

  1. error: passing ‘const std::map<int, int>’ as ‘this’ argument discards qualifiers [-fpermissive]
  2. C++ "error: passing 'const std::map<int, std::basic_string<char> >' as 'this' argument of ..."

But in both of them, it was because of the fact that std::map has been declared as const. On the other hand, the map I'm trying to use/access has not been declared as const, and my error is also related to a lambda. As you can see, I'm creating a copy of the original my_map in the lambda capture list as map_lambda = my_map. So, why am I'm getting this -fpermissive error? Or, when we capture something in a lambda, does it automatically get converted to const?

Detailed error message:

main.cpp: In lambda function:
main.cpp:26:32: error: passing ‘const std::map<int, bool>’ as ‘this’ argument discards qualifiers [-fpermissive]
         return map_lambda[pt.id];
                                ^
In file included from /usr/include/c++/7/map:61:0,
                 from main.cpp:2:
/usr/include/c++/7/bits/stl_map.h:484:7: note:   in call to ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = bool; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, bool> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = bool; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’
       operator[](const key_type& __k)
       ^~~~~~~~

On a side note, I know the operator[] returns the value if the key already exists, otherwise it creates a new key and inserts the value (and returns it). But why does const for std::map::operator[] give a compilation error? Shouldn't it be more of like a runtime error instead?

Upvotes: 1

Views: 1007

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 311048

The problem is that the lambda expression used in this statement

 points_vec.erase(std::remove_if(points_vec.begin(), points_vec.end(), [map_lambda = my_map] (const Point pt) -> bool {
        return map_lambda[pt.id];
    }), points_vec.end());

is immutable. So map_lambda is considered as a constant object. But the subscript operator requires that the object would be modifiable. That is you may not use the subscript operator with a constant object of the class template std::map.

You should declare map_lambda as a non-constant reference to my_map.

That is change the lambda like

[&map_lambda = my_map] (const Point pt) -> bool {
        return map_lambda[pt.id];
    }

Upvotes: 1

Yksisarvinen
Yksisarvinen

Reputation: 22334

All variables captured by lambda are implicitly const, unless you mark lambda as mutable. There is no const overload for operator[] for map, because it may always change the map (create new key-value pair).

If you are sure that every pt.id exists in the map, change operator [] to at() call:

[map_lambda = my_map] (const Point pt) -> bool {
        return map_lambda.at(pt.id);
}

If you want to create keys in the map if they are not present, change lambda to mutable:

[map_lambda = my_map] (const Point pt) mutable -> bool {
        return map_lambda[pt.id];
}

Upvotes: 4

Related Questions