2016-03-26 5 views
3

インパクトby Antony's Williams "C++ Concurrency in Action"スレッドセーフなハッシュマップを見てみたいと思います。私は、そのコードをコピーして、いくつかの出力演算子を追加し、これは私が思い付いたものです:const_iterator型の戻り値からイテレータへの実行可能な変換がありません

#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++ -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

何私はここで行方不明ですか?

答えて

7

これは予期された動作です。 find_entry_forでは返品タイプiteratorと一致しないconst_iteratorを返そうとしています。

find_entry_fordata.begin()ため、dataがそれにconst std::list<bucket_value>begin()と呼ばれる、constメンバ関数ですconst_iteratorを返します。また、std::find_ifは、戻りタイプがfind_entry_forに暗黙的に変換されなかったパラメータイテレータのタイプ、つまりconst_iteratorを返します。つまり、bucket_iteratorstd::list::iterator)です。

返される反復子は、それが指す値を変更するために使用される可能性がありますので、あなたは可能性があり

  1. 変更find_entry_for非constメンバ関数へ。 (新しいオーバーロード関数として追加する場合は元のconstメンバ関数の戻り値の型をconst_iteratorに変更してください)。

  2. 返す前にconvert const_iterator to iteratorにお試しください。

+0

の修正は、 'const_iterator'を返すために、その関数を作成することです。 C++ 11はそれらを実際に便利にしました。データを変更する必要がある場合は、const以外のオーバーロードも追加できます。 –

3

bucket_iterator、それは一定のイテレータではないです以下の方法

typedef typename bucket_data::iterator bucket_iterator; 

を定義しています。このメンバー関数は、修飾子constと宣言され、一定のデータメンバーdataためのオーバーロード機能beginendが使用されているので、メンバ関数におけるしかしながら

find_entry_for

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

標準アルゴリズムstd::find_ifは、一定の反復子を使用します。

したがって、クラスの定数イテレータを定義し、それを関数の戻り値の型として使用する必要があります。

typedef typename bucket_data::const_iterator const_bucket_iterator; 
+0

... 'find_entry_for'の別の定義を' const'指定子なしで追加して、イテレータを返すような修正を許可しますか?BTWは、 'const'メソッドを呼び出すと、その内部で' const'メソッドを呼び出すことを明確にしてくれてありがとう。私はその点を逃した。 – Patryk

関連する問題