c++并发编程实战中文版(第二版)201页的threadsafe_lookup_table代码疑似有误

博客指出《C++并发编程实战》中文版(第二版)第201页的threadsafe_lookup_table代码存在一个问题,导致编译报错。错误源于在const成员函数中,调用了返回const_iterator的begin()和end(),但后续操作需要读写迭代器。通过分析源码,博主揭示了解决方案,即定义新的只读迭代器类型,并为find_entry_for添加非const重载版本,同时调整接口函数中迭代器的使用,从而避免从const_iterator到iterator的非法转换。

照着抄会直接报错, 后来我排查了很久才发现问题所在


问题排查

报错原因是这样的(手动分行)

No viable conversion from returned value of type
'std::_List_const_iterator<std::_List_val<std::_List_simple_types<std::pair<int, std::basic_string<char>>>>>'
to function return type 'typename list<bucket_value>::iterator'
(aka '_List_iterator<_List_val<std::_List_simple_types<std::pair<int, std::basic_string<char>>>>>')

把第二行和第四行的类型提取出来, 可以看到大概意思应该是说不能把const的迭代器转为非const的迭代器

'std::_List_const_iterator<std::_List_val<std::_List_simple_types<std::pair<int, std::basic_string<char>>>>>'
________________'_List_iterator<_List_val<std::_List_simple_types<std::pair<int, std::basic_string<char>>>>>'

 


首先, 查看list的源码可以看到list::begin()是有两个版本的(end()也一样)

区别是一个返回iterator, 另一个返回const_iterator且函数后面有const

当同样在加了const的成员函数里调用begin()会返回第二个版本, 即只读迭代器const_iterator

而书里的源码是这样的

using bucket_data = list<bucket_value>;
using bucket_iterator = typename bucket_data::iterator;
bucket_iterator find_entry_for(Key const &key) const
{
    return find_if(data.begin(), data.end(),
                   [&](bucket_value const &item){return item.first == key;});
}

 find_entry_for()函数声明后面加了const, 于是里面的data.begin()和data.end()会调用上面的第二个版本, 即返回const_iterator,  进而find_if()找到的迭代器也会是const_iterator  

然而函数声明的迭代器却是读写迭代器(iterator), 无法从只读迭代器(const_iterator)转换为读写迭代器(iterator), 从而报错


验证如下

#include<bits/stdc++.h>
using namespace std;
class A
{
public:
    int get_value() const
    {
        return 1;
    }

    int get_value()
    {
        return 2;
    }
};
class B
{
    A member_a;
public:
    void print_A_value()
    {
        A local_a;
        printf("member_a:%d\n", member_a.get_value());
        printf(" local_a:%d\n", local_a.get_value());
    }

    void print_A_value_const() const
    {
        A local_a;
        printf("add const member_a:%d\n", member_a.get_value());
        printf("add const  local_a:%d\n", local_a.get_value());
    }
};

int main()
{
    B b;
    b.print_A_value();
    b.print_A_value_const();
    return 0;
}

member_a:2
 local_a:2
add const member_a:1
add const  local_a:2

可以看到其中第三行输出了1

从中可以看到触发是有条件的, 操作的变量只能为成员变量, 不能是局部变量(全局变量也不行)

从而验证了上面对报错原因的猜测(会根据函数声明有无const, 进而调用不同的成员变量重载函数)

我的修改方法如下, 首先声明一个只读迭代器类型bucket_const_iterator

再为find_entry_for添加一个不加const的重载版本

using bucket_iterator = typename bucket_data::iterator ;
using bucket_const_iterator = typename bucket_data::const_iterator ;
bucket_const_iterator find_entry_for(Key const &key) const
{
    return find_if(data.begin(), data.end(),[&](bucket_value const &item){return item.first == key;});
}
bucket_iterator find_entry_for(Key const &key)
{
    return find_if(data.begin(), data.end(),[&](bucket_value const &item){return item.first == key;});
}

同时发现3个接口函数里只有value_for()声明里加了const, 会调用加const版本的find_entry_for() 

且value_for()内部不需要修改found_entry, 所以修改局部迭代器变量声明为bucket_const_iterator

其他函数则调用不加const版本的获取读写迭代器bucket_iterator

Value value_for(Key const&key,Value const&default_value) const
{
    shared_lock lock(m);
    bucket_const_iterator found_entry= find_entry_for(key);
    return (found_entry==data.end())?default_value:found_entry->second;
}

最后成功运行!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值