将“const std::map<int, bool>”作为“this”参数传递会丢弃 lambda 函数中的限定符 [-fpermissive]

Posted

技术标签:

【中文标题】将“const std::map<int, bool>”作为“this”参数传递会丢弃 lambda 函数中的限定符 [-fpermissive]【英文标题】:Getting passing ‘const std::map<int, bool>’ as ‘this’ argument discards qualifiers [-fpermissive] in lambda function 【发布时间】:2021-07-23 06:54:14 【问题描述】:

这是我收到错误的基本代码 sn-p:

错误:将“const std::map”作为“this”参数传递会丢弃限定符 [-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;

注意:我知道我可以不使用std::map 删除点,但上面的代码 sn-p 只是一个更大问题的示例。

我检查了一些关于类似错误的问题:

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

但在他们两个中,这是因为std::map 已被声明为const。另一方面,我尝试使用/访问的map 尚未声明为const,我的错误也与lambda 有关。如您所见,我在 lambda 捕获列表中将原始 my_map 的副本创建为 map_lambda = my_map。那么,为什么我会收到这个 -fpermissive 错误?或者,当我们在 lambda 中捕获某些内容时,它是否会自动转换为 const

详细的错误信息:

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)
       ^~~~~~~~

顺便说一句,我知道operator[] 如果键已经存在则返回值,否则它会创建一个新键并插入值(并返回它)。但是为什么const for std::map::operator[] 会出现编译错误呢?它不应该更像是运行时错误吗?

【问题讨论】:

【参考方案1】:

所有由 lambda 捕获的变量都是隐式的 const,除非你将 lambda 标记为 mutableoperator[] 的 map 没有 const 重载,因为它可能总是会更改 map(创建新的键值对)。

如果您确定地图中存在每个pt.id,请将operator [] 更改为at() 调用:

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

如果您想在地图中创建不存在的键,请将 lambda 更改为 mutable

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

【讨论】:

请注意,在[map_lambda = my_map] 中,map_lambda 将是my_map副本,因此无论发生什么,my_map 都不会以任何方式改变到map_lambda。如果您需要更新my_map,请改为通过引用来捕获它,例如[&amp;map_lambda = my_map],或者甚至只是[&amp;my_map][&amp;],因为在此示例中它实际上不需要单独的名称。 @RemyLebeau 感谢您提供这些提示 :) 只是为了确认,除了通过引用捕获 my_map 之外,我还必须将 lambda 更改为 mutable,对吗?因为,正如上面提到的@Yksisarvinen,没有mutable,它会想要const 参考,对吧?如果我错了,请纠正我。谢谢! @Milan "我还必须将 lambda 更改为 mutable,对吗?" - 是的,您需要 mutable,否则 my_map 将被捕获const 引用,从而阻止使用 map::operator[] @Milan “这不应该是运行时错误吗?” - 不。允许在 const 对象上调用非常量方法是没有意义的。并且operator[] 必须是非常量的,因此它可以改变map 的内容。这只是 C++ 中 const-correctness 工作原理的一部分 让我们continue this discussion in chat。【参考方案2】:

问题在于该语句中使用的 lambda 表达式

 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());

是不可变的。所以map_lambda 被认为是一个常量对象。但是下标运算符要求对象是可修改的。那就是你不能将下标运算符与类模板std::map的常量对象一起使用。

您应该将map_lambda 声明为对my_map 的非常量引用。

那就是像改变 lambda 一样

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

【讨论】:

感谢来自莫斯科的@Vlad 的回答...但正如@Yksisarvinen 上面指出的那样,除非我使用mutable,否则在lambda 中捕获的任何内容都会隐式转换为const,对吗?那么,这不是const 参考吗?如果我错了,请纠正我。再次感谢! @Milan 我展示了如何在 lambda 中声明对原始地图的非常量引用。 感谢您的回复,但如果没有mutable[&amp;map_lambda = my_map] 不会是const 的参考吗?抱歉,但现在我有点困惑,因为正如@Remy Lebeau 指出的那样,我需要mutable,否则它将是const 参考! @Milan 不,不会。成为一个恒定的参考 谢谢@Vlad,我试过你说的,看起来如果我在捕获列表中使用引用,那么即使没有mutable,我也无法修改捕获的对象。所以,只是为了确认,mutable 只有当我按价值捕获某些东西时才会出现,对吧?如果我错了,请纠正我。再次感谢:)

以上是关于将“const std::map<int, bool>”作为“this”参数传递会丢弃 lambda 函数中的限定符 [-fpermissive]的主要内容,如果未能解决你的问题,请参考以下文章

如何将thinkcmf导入eclipse

如何将Ios文件上传到

Javascript 将正则表达式 \\n 替换为 \n,将 \\t 替换为 \t,将 \\r 替换为 \r 等等

如何将视频文件转换格式

sh 一个将生成CA的脚本,将CA导入到钥匙串中,然后它将创建一个证书并与CA签名,然后将其导入到

python怎么将0写入文件?