C ++如何使用模板类的模板类子项从哈希表中实现哈希集?

时间:2015-07-15 22:16:28

标签: c++ templates c++11 hashtable hashset

我有一个学校作业来实现一个hash_set和一个hash_map,我得到一个main.cpp,它有代码来测试这些类(插入,删除,搜索,打印等)。我还给了一个基础的hash_table类,它是一个模板化的类,并且只是告诉“使用自己的新类来使hash_set和hash_map基于测试代码工作”。

如果没有更具体的细节,我假设我应该基于hash_table实现集合和映射,老师说项目可以在两个新类之间的40行代码(wat ??)中完成。但是我甚至无法让hash_set类继承hash_table,因为它是一个模板。我可以在不将hash_set类作为模板的情况下编译好,直到我实例化它,因为测试代码在声明中传递了一个模板类型。但是,如果我尝试使类本身成为模板类型来补偿这一点,那么一切都会中断,我无法追踪错误的来源。我大约8个小时进入这个项目应该是一个小时的项目,所以我在这里结束了。

以下是带有测试代码的原始main.cpp(hash_map已被注释掉,因为我还没有开始:/)

//
//  main.cpp
//  CS3100Project05_HashCollections
#include "WSUHashTable.h"
//#include "WSUHashMap.h"  // Uncomment to test WSUHashMap
#include "WSUHashSet.h"  // Uncomment to test WSUHashSet
#include <iostream>

int main(int argc, const char * argv[])
{
   ///////////////////////////////////////////////////////////////////
   /// Part 1 :TEST HASH TABLE                                     ///
   ///////////////////////////////////////////////////////////////////
   {
      WSUHashTable<int> example = {1, 2, 3, 4, 1};

      std::cout << "Part 1a: Expected output is in any order " <<
         "\"1 2 3 4\"\n";

      for(int n : example)
      {
         std::cout << n << ' ';
      }
      std::cout << std::endl;
      std::cout << std::endl;

      std::cout << "Part 1b: Expected output is \"Found 2\"\n";
      {
         auto search = example.find(2);
         if(search != example.end())
         {
            std::cout << "Found " << (*search) << '\n';
         }
         else
         {
            std::cout << "Not found\n";
         }
      }
      std::cout << std::endl;

      std::cout << "Part 1c: Expected output is \"Not found\"\n";
      example.erase(2);
      {
         auto search = example.find(2);
         if(search != example.end()) {
            std::cout << "Found " << (*search) << '\n';
         }
         else {
            std::cout << "Not found\n";
         }
      }
      std::cout << std::endl;
   }

   ///////////////////////////////////////////////////////////////////
   /// Part 2: TEST HASH SET                                       ///
   ///////////////////////////////////////////////////////////////////
   /***** Uncomment to test WSUHashSet */

   {
      WSUHashSet<std::string> stringSet = {"1", "2", "3", "4"};

      std::cout << "Part 2a: Expected output is \"Found \"2\"\"\n";
      {  // Test find() that succeeds
         auto search = stringSet.find("2");
         if(search != stringSet.end()) {
            std::cout << "Found \"" << (*search) << "\"\n";
         }
         else {
            std::cout << "Not found\n";
         }
      }
      std::cout << std::endl;

      std::cout << "Part 2b: Expected output is \"Not found\"\n";
      stringSet.erase("2");
      {  // Test find() that fails
         auto search = stringSet.find("2");
         if(search != stringSet.end())
         {
            std::cout << "Found \"" << (*search) << "\"\n";
         }
         else
         {
            std::cout << "Not found\n";
         }
      }
      std::cout << std::endl;

      WSUHashSet<double> doubleSet = {
         1.1, 2.2, 3.2, 4.4, 5.5, 6.1, 7.2, 8.4, 9.9
      };

      std::cout << "Part 2c: Expected output is in any order " <<
         "\"5.5 7.2 8.4 9.9 1.1 2.2 3.2 4.4 6.1\"\n";
      for(auto n : doubleSet )
      {  // Test using implicit iterators
         std::cout << n << ' ';
      }
      std::cout << std::endl;
      std::cout << std::endl;

      std::cout << "Part 2d: Expected output is in any order " <<
         "\"5.5 7.2 8.4 9.9 4.4 6.1\"\n";
      // Part 7: Using explicit iterators while mutating set
      for(auto it = doubleSet.begin(); it != doubleSet.end(); )
      {  // erase every number less than 4.0
         if(*it < 4.0)
         {
            it = doubleSet.erase(it);
         }
         else
         {
            ++it;
         }
      }

      for(auto n : doubleSet)
      {
         std::cout << n << ' ';
      }
      std::cout << std::endl;
      std::cout << std::endl;

   }

   ///////////////////////////////////////////////////////////////////
   /// Part 3: TEST HASH MAP                                       ///
   ///////////////////////////////////////////////////////////////////
   /***** Uncomment to test WSUHashMap *
   {
      WSUHashMap<int, std::string> dict = {{1, "one"}, {2, "two"}};
      dict.insert({3, "three"});
      dict.insert(std::make_pair(4, "four"));
      dict.insert({{4, "another four"}, {5, "five"}});

      std::cout << "Part 3a: Expected output is " <<
         "\"inserting 1 -> \"another one\" failed\"\n";
      bool ok = dict.insert({1, "another one"}).second;
      std::cout << "inserting 1 -> \"another one\" "
         << (ok ? "succeeded" : "failed") << '\n';
      std::cout << std::endl;

      std::cout << "Part 3b: Expected output is " <<
         "\"contents: " <<
         "1 => one " <<
         "2 => two " <<
         "3 => three " <<
         "4 => four " <<
         "5 => five\"\n";
      std::cout << "contents: ";
      for(auto& p : dict)
      {
         std::cout << " " << p.first << " => " << p.second << ' ';
      }
      std::cout << std::endl;
   }
   *****/

   return 0;
}

这是原始的hash_table类文件:

//
//  WSUHashTable.h
//  CS3100Project05_HashCollections

#ifndef __CS3100Project05_HashCollections__WSUHashTable_H
#define __CS3100Project05_HashCollections__WSUHashTable_H

#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include <cassert>


//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
template <
   typename elementT,
   typename Hash = std::hash<elementT>
>
class WSUHashTable
{
private:
   ///////////////////////////////////////////////////////////////////
   typedef elementT bucket_t;

   ///////////////////////////////////////////////////////////////////
   static const std::size_t initialCapacity = (1 << 2);
   constexpr static float maxLoadFactor = 0.73f;

   ///////////////////////////////////////////////////////////////////
   std::size_t mSize;               // Number of elements in table
   std::vector<bucket_t> mStorage;  // Storage for elements
   std::vector<bool> mIsValidFlags;

public:

   ///////////////////////////////////////////////////////////////////
   class iterator : public std::iterator<
   std::input_iterator_tag, elementT>
   {
      friend class WSUHashTable<elementT, Hash>;

      const WSUHashTable<elementT, Hash> *mHashTablePtr;
      std::size_t mIndex;

      ////////////////////////////////////////////////////////////////
      iterator(
         const WSUHashTable<elementT, Hash> &hashTable,
         std::size_t index = 0
      ) :
      mHashTablePtr(&hashTable),
      mIndex(index)
      {
      }

      ////////////////////////////////////////////////////////////////
      std::size_t getIndex() const
      {
         return mIndex;
      }

   public:

      ////////////////////////////////////////////////////////////////
      iterator(const iterator &other) :
      mHashTablePtr(other.mHashTablePtr),
      mIndex(other.mIndex)
      {
      }

      ////////////////////////////////////////////////////////////////
      iterator& operator++()
      {
         ++mIndex;
         while(mIndex < mHashTablePtr->mIsValidFlags.size() &&
               !mHashTablePtr->mIsValidFlags[mIndex])
         {  // Skip over empty buckets
            ++mIndex;
         }
         return *this;
      }

      ////////////////////////////////////////////////////////////////
      iterator operator++(int)
      {
         const_iterator tmp(*this);
         operator++();
         return tmp;
      }

      ////////////////////////////////////////////////////////////////
      bool operator==(const iterator& rhs)
      {
         return mIndex == rhs.mIndex;
      }

      ////////////////////////////////////////////////////////////////
      bool operator!=(const iterator& rhs)
      {
         return mIndex != rhs.mIndex;
      }

      ////////////////////////////////////////////////////////////////
      const elementT &operator*()
      {
         return mHashTablePtr->mStorage[mIndex];
      }
   };

   ///////////////////////////////////////////////////////////////////
   typedef const iterator const_iterator;

private:
   typedef std::pair<const_iterator, bool> _findResult;

   ///////////////////////////////////////////////////////////////////
   std::size_t _calculatedIndex(const elementT &element) const
   {
      return (Hash()(element) % mStorage.size());
   }

   ///////////////////////////////////////////////////////////////////
   // Returns a pair containing iterator to bucket where element
   // should be and flag indicating whether it is there.
   _findResult _find( const elementT &element ) const
   {
      std::size_t index = _calculatedIndex(element);
      while(mIsValidFlags[index] &&
         ((mStorage[index] < element) || (element < mStorage[index])))
      {  // Loop until element is found or an empty bucket is found
         ++index;
         index %= mStorage.size();
      }

      return _findResult(
         const_iterator(*this, index), mIsValidFlags[index]);
   }

   ///////////////////////////////////////////////////////////////////
   void _doubleCapacityAndRehash()
   {
      const std::size_t oldSize = mIsValidFlags.size();

      // Save off mStorage by moving contents instead of copying
      std::vector<bucket_t> oldStorage = std::move(mStorage);
      std::vector<bool> oldIsValidFlags = std::move(mIsValidFlags);

      // Replace mStorage and mIsValidFlags with empty storage with
      // twice the size/capacity.
      mStorage = std::move(std::vector<bucket_t>(oldSize * 2));
      mIsValidFlags = std::move(std::vector<bool>(oldSize * 2));

      // We are going to re-insert everything, so strat with size 0
      mSize = 0;

      for(std::size_t i = 0; i < oldSize; ++i)
      {  // Insert values from all valid buckets in old storage
         if(oldIsValidFlags[i])
         {
            insert(oldStorage[i]);
         }
      }
   }

public:

   ///////////////////////////////////////////////////////////////////
   WSUHashTable() :
   mSize(0),
   mStorage(initialCapacity),
   mIsValidFlags(initialCapacity)
   {
   }

   ///////////////////////////////////////////////////////////////////
   WSUHashTable(const WSUHashTable &other) :
   mSize(other.mSize),
   mStorage(other.mStorage),
   mIsValidFlags(other.mIsValidFlags)
   {
   }

   ///////////////////////////////////////////////////////////////////
   template< class InputIt >
   WSUHashTable(InputIt first, InputIt last)  :
   mSize(0),
   mStorage(initialCapacity),
   mIsValidFlags(initialCapacity)
   {
      while(first != last)
      {
         insert(*first);
         ++first;
      }
   }

   ///////////////////////////////////////////////////////////////////
   WSUHashTable(std::initializer_list<elementT> init)  :
   mSize(0),
   mStorage(initialCapacity),
   mIsValidFlags(initialCapacity)
   {
      insert(init);
   }

   ///////////////////////////////////////////////////////////////////
   std::size_t size() const
   {
      return mSize;
   }

   ///////////////////////////////////////////////////////////////////
   /// Inserts element(s) into the container, if the container doesn't
   /// already contain an an equivalent element.
   /// Returns a pair consisting of an iterator to the inserted
   /// element (or to the element that prevented the insertion) and a
   /// bool denoting whether the insertion took place.
   std::pair<const_iterator, bool> insert( const elementT &element )
   {
      if(mSize > (maxLoadFactor * mStorage.size()))
      {  // resize to make room for insertion
         _doubleCapacityAndRehash();
      }

      _findResult result = _find(element);

      if(result.second)
      {  // element is present
         return std::pair<const_iterator, bool>(result.first, false);
      }

      const std::size_t index = result.first.getIndex();
      mStorage[index] = element;
      mIsValidFlags[index] = true;
      ++mSize;

      return std::pair<const_iterator, bool>(result.first, true);
   }


   ///////////////////////////////////////////////////////////////////
   /// Inserts element(s) into the container, if the container doesn't
   /// already contain an an equivalent element.
   /// Returns a pair consisting of an iterator to the inserted
   /// element (or to the element that prevented the insertion) and a
   /// bool denoting whether the insertion took place.
   ///
   /// An && argumnet signals an "emplace" operation in C++11. The
   /// value will be moved via std::move() instead of copied.
   std::pair<const_iterator, bool> insert( elementT &&element )
   {
      if(mSize > (maxLoadFactor * mStorage.size()))
      {  // resize to make room for insertion
         _doubleCapacityAndRehash();
      }

      _findResult result = _find(element);

      if(result.second)
      {  // element is present
         return std::pair<const_iterator, bool>(
            result.first, false);
      }

      const std::size_t index = result.first.getIndex();
      mStorage[index] = std::move(element);
      mIsValidFlags[index] = true;
      ++mSize;

      return std::pair<const_iterator, bool>(result.first, true);
   }


   ///////////////////////////////////////////////////////////////////
   void insert( std::initializer_list<elementT> ilist )
   {
      for(const elementT &element : ilist)
      {
         insert(element);
      }
   }

   ///////////////////////////////////////////////////////////////////
   /// Returns iterator following the last removed element.
   const_iterator erase( const_iterator pos )
   {
      const std::size_t index = pos.getIndex();

      if(mIsValidFlags[index])
      {  // element is present
         mIsValidFlags[index] = false;
         --mSize;
      }

      return ++iterator(pos);
   }

   ///////////////////////////////////////////////////////////////////
   // Returns the number of elements erased (it will be zero or one).
   std::size_t erase(const elementT &element)
   {
      _findResult result = _find(element);

      if(result.second)
      {  // element is present
         mIsValidFlags[result.first.getIndex()] = false;
         --mSize;
         return 1;
      }

      return 0;
   }

   ///////////////////////////////////////////////////////////////////
   const_iterator find( const elementT &element ) const
   {
      _findResult result = _find(element);

      if(result.second)
      {  // element was found
         return result.first;
      }

      return end();
   }

   ///////////////////////////////////////////////////////////////////
   const_iterator begin() const
   {
      std::size_t index = 0;
      while(index < mIsValidFlags.size() && !mIsValidFlags[index])
      {  // Skip over empty buckets
         ++index;
      }
      return const_iterator(*this, index);
   }

   ///////////////////////////////////////////////////////////////////
   const_iterator end() const
   {
      return const_iterator(*this, mStorage.size());
   }
};

#endif /* defined(__CS3100Project05_HashCollections__WSUHashTable_H) */

我知道hash_table文件很长,而且我对如何继续进行了一个不错的想法,因为hash_set实际上只需要在插入中实现一个搜索功能,以确保列表是唯一的,但并不是真的搞得一团糟使用哈希顺序或任何东西(我不确定如何实现哈希本身,但我假设继承将很好地排序),并且hash_map将允许空键和任何空值....但首先是必须编译。

这就是我目前作为我的hash_set类,只是试图获得一个使用父类的构造函数的正确类型(std :: string)的简单构造函数:

#ifndef __WSUHashSet__
#define __WSUHashSet__


#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include "WSUHashTable.h"


/*template<
    typename elementT,
    typename Hash = std::hash<elementT>
    >*/
class WSUHashSet : public WSUHashTable<std::string> {

private:

public:

    WSUHashSet(std::initializer_list<std::string> init) :  WSUHashTable(init)
    {
        insert(init);
    }



};




#endif  //__WSUHashSet__

目前我得到了同样的错误:使用这个确切的代码它告诉我“WSUHashSet不是模板”,这很好,因为我的类很好,但很糟糕,因为我不能只编辑main.cpp而不是把它当作模板。

当我尝试将其设为模板时,如下所示:

#ifndef __WSUHashSet__
#define __WSUHashSet__


#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include "WSUHashTable.h"


template<
    typename elementT,
    typename Hash = std::hash<elementT>
    >
class WSUHashSet<elementT> : public WSUHashTable<std::string> {

private:

public:

    WSUHashSet<elementT>::WSUHashSet(std::initializer_list<std::string> init) :  WSUHashTable(init)
    {
        insert(init);
    }



};




#endif  //__WSUHashSet__

我真的需要一些方向。我不能花更多的时间在这上面,因为它不是我唯一的课程,我觉得这应该是相当简单的。这个理论是有道理的,但实施让我觉得我一味地在圈内徘徊。

由于

编辑:实际的编译器错误是

WSUHashSet.h第19行:错误:WSUHashSet不是模板

显然,粘贴到代码块中会引起混淆。这是打破编译的实际行(代码块中的19):

WSUHashSet(std::initializer_list<std::string> init) :  WSUHashTable(init)
    {
        insert(init);
    }

1 个答案:

答案 0 :(得分:0)

如果查看测试代码,您可以看到您创建的类型必须是模板,因为它们是针对特定元素类型实例化的:

WSUHashTable<int> example = {1, 2, 3, 4, 1};

WSUHashSet<double> doubleSet = { ...

您展示了制作模板的尝试:

template<
    typename elementT,
    typename Hash = std::hash<elementT>
>
class WSUHashSet<elementT> : public WSUHashTable<std::string> {
  public:
    WSUHashSet<elementT>::WSUHashSet(std::initializer_list<std::string> init) :  WSUHashTable(init)
    {
        insert(init);
    }
    ...

那不是那么远......试试:

template <typename elementT>
class WSUHashSet<elementT> : public WSUHashTable<elementT>
{
  public:
    WSUHashSet(std::initializer_list<std::string> init)
      :  WSUHashTable<elementT>(init)
    { }