c++ - 没有从 const_iterator 类型的返回值到迭代器的可行转换

标签 c++ templates c++11 concurrency c++14

灵感来自 Antony's Williams "C++ Concurrency in Action"我想仔细看看他的线程安全 HashMap 。我复制了它的代码并添加了一些输出运算符,这就是我想出的:

#include <boost/thread/shared_mutex.hpp>
#include <functional>
#include <list>
#include <mutex>
#include <iostream>

template <typename Key, typename Value, typename Hash = std::hash<Key>>
class thread_safe_hashmap
{
private:
  class bucket_type
  {
  public:
    typedef std::pair<Key, Value> bucket_value;
    typedef std::list<bucket_value> bucket_data;
    typedef typename bucket_data::iterator bucket_iterator;

    bucket_data data;
    mutable boost::shared_mutex mutex;

    bucket_iterator find_entry_for(const Key& key) const
    {
      return std::find_if(data.begin(), data.end(),
                          [&](const bucket_value& item) { return item.first == key; });
    }

  public:
    void add_or_update_mapping(Key const& key, Value const& value)
    {
      std::unique_lock<boost::shared_mutex> lock(mutex);
      bucket_iterator found_entry = find_entry_for(key);
      if (found_entry == data.end())
      {
        data.push_back(bucket_value(key, value));
      }
      else
      {
        found_entry->second = value;
      }
    }
  };

  std::vector<std::unique_ptr<bucket_type>> buckets;
  Hash hasher;

  bucket_type& get_bucket(Key const& key) const
  {
    std::size_t const bucket_index = hasher(key) % buckets.size();
    return *buckets[bucket_index];
  }

  template <typename Key2, typename Value2>
  friend std::ostream& operator<<(std::ostream& os, const thread_safe_hashmap<Key2, Value2>& map);

public:
  thread_safe_hashmap(unsigned num_buckets = 19, Hash const& hasher_ = Hash())
      : buckets(num_buckets), hasher(hasher_)
  {
    for (unsigned i = 0; i < num_buckets; ++i)
    {
      buckets[i].reset(new bucket_type);
    }
  }

  thread_safe_hashmap(thread_safe_hashmap const& other) = delete;
  thread_safe_hashmap& operator=(thread_safe_hashmap const& other) = delete;

  void add_or_update_mapping(Key const& key, Value const& value)
  {
    get_bucket(key).add_or_update_mapping(key, value);
  }
};

template <typename First, typename Second>
std::ostream& operator<<(std::ostream& os, const std::pair<First, Second>& p)
{
  os << p.first << ' ' << p.second << '\n';
  return os;
}

template <typename Key, typename Value>
std::ostream& operator<<(std::ostream& os, const thread_safe_hashmap<Key, Value>& map)
{
  for (unsigned i = 0; i < map.buckets.size(); ++i)
  {
    for (const auto el : map.buckets[i]->data) os << el << ' ';
    os << '\n';
  }

  return os;
}


int main()
{
  thread_safe_hashmap<std::string, std::string> map;
  map.add_or_update_mapping("key1", "value1");  // problematic line
  std::cout << map;
}

标记的行在 gcc 和 clang 上都引起了问题:

clang++  -Wall -std=c++14 main2.cpp -lboost_system -o main
main2.cpp:24:14: error: no viable conversion from returned value of type 'std::_List_const_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' to function
      return type 'bucket_iterator' (aka '_List_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >')
      return std::find_if(data.begin(), data.end(),
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main2.cpp:32:37: note: in instantiation of member function 'thread_safe_hashmap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, std::hash<string> >::bucket_type::find_entry_for'
      requested here
      bucket_iterator found_entry = find_entry_for(key);
                                    ^
main2.cpp:71:21: note: in instantiation of member function 'thread_safe_hashmap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, std::hash<string>
      >::bucket_type::add_or_update_mapping' requested here
    get_bucket(key).add_or_update_mapping(key, value);
                    ^
main2.cpp:98:7: note: in instantiation of member function 'thread_safe_hashmap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, std::hash<string> >::add_or_update_mapping'
      requested here
  map.add_or_update_mapping("key1", "value1");
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/stl_list.h:125:12: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
      'std::_List_const_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' to 'const std::_List_iterator<std::pair<std::__cxx11::basic_string<char>,
      std::__cxx11::basic_string<char> > > &' for 1st argument
    struct _List_iterator
           ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/stl_list.h:125:12: note: candidate constructor (the implicit move constructor) not viable: no known conversion from
      'std::_List_const_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' to 'std::_List_iterator<std::pair<std::__cxx11::basic_string<char>,
      std::__cxx11::basic_string<char> > > &&' for 1st argument
1 error generated.

melpon's online demo

我在这里错过了什么?

最佳答案

这是预期的行为。在 find_entry_for你正试图返回 const_iterator ,与返回类型不匹配 iterator .

find_entry_for是 const 成员函数,对于 data.begin() , data将是 const std::list<bucket_value> , begin() 调用它会返回 const_iterator .和 std::find_if 将返回与参数迭代器类型相同的类型,即 const_iterator , 无法隐式转换为 find_entry_for 的返回类型,即 bucket_iterator (std::list::iterator)。

因为返回的迭代器可能被用来改变它指向的值,你可以

  1. 更改 find_entry_for到非常量成员函数。 (或者添加为新的重载函数,将原来的const成员函数的返回类型改为const_iterator。)

  2. 尝试convert const_iterator to iterator 返回前。

关于c++ - 没有从 const_iterator 类型的返回值到迭代器的可行转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36236556/

相关文章:

c++ - QSqlDatabase::open() 总是返回 true

C++ 访问继承类的成员,其中继承类是模板参数

c++ - 如何在 C++ 中使用 openmp 并行运行展开的 'for' 循环(tmp)?

c++ - 如何确保一个方法在该对象的生命周期内只执行一次?

c++ - 如何从 C++(winapi) 中的资源加载 GDI::Image (GIF)?

c++ - mem_func 和 for_each 用法

c++ - "error: C2275: ' QMouseEvent ' : illegal use of this type as an expression"

c++ - 为什么这部分代码被忽略了?

c++ - 当构造函数具有相同的参数类型(文件路径)时,如何从数组创建(初始化)std::tuple

c++ - 是否有标准模板类用于使用指针管理对象并提供复制/move/分配操作?