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

C++如何使用模板类的模板类子类从哈希表实现哈希集?,c++,templates,c++11,hashtable,hashset,C++,Templates,C++11,Hashtable,Hashset,我有一个学校作业要实现一个hash_集和一个hash_映射,我得到了一个main.cpp,它有代码来测试这些类的插入、删除、搜索、打印等。我还得到了一个基本hash_表类,它是一个模板类,只是告诉我让hash_集和hash_映射在测试代码的基础上使用它们自己的新类 如果没有更具体的细节,我假设我应该基于hash_表实现set和map,老师说这个项目可以用40行代码完成??在两个新类之间…但是我甚至不能让hash_set类继承hash_表,因为它是一个模板。由于测试代码在声明中传递了一个模板类型,

我有一个学校作业要实现一个hash_集和一个hash_映射,我得到了一个main.cpp,它有代码来测试这些类的插入、删除、搜索、打印等。我还得到了一个基本hash_表类,它是一个模板类,只是告诉我让hash_集和hash_映射在测试代码的基础上使用它们自己的新类

如果没有更具体的细节,我假设我应该基于hash_表实现set和map,老师说这个项目可以用40行代码完成??在两个新类之间…但是我甚至不能让hash_set类继承hash_表,因为它是一个模板。由于测试代码在声明中传递了一个模板类型,所以在实例化它之前,我可以在不将hash_set类设置为模板的情况下让它编译为ok。但是,如果我试图使类本身成为一个模板类型来补偿这一点,那么一切都会崩溃,我无法追踪错误的来源。我大概花了8个小时在这个项目上,应该是1个小时的项目,所以我在这里束手无策

下面是带有测试代码hash_映射的原始main.cpp被注释掉,因为我还没有开始:/

//
//  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;
}
这是原始哈希表类文件:

//
//  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_表文件很长,我对如何继续有一个不错的想法,因为hash_集实际上只需要在insert中实现一个搜索函数,以确保列表是唯一的,但不会真正扰乱hash顺序或任何我不确定如何实现hash本身的事情,但是我假设继承会很好地解决这个问题,hash_映射将允许一个空键和任何空值……但是首先必须编译一些东西

这就是我目前作为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__
我真的需要一些指导。我不能在这上面花太多时间,因为这不是我唯一的课程,我觉得这应该是相当简单的。这个理论是有道理的,但是这个实现让我觉得我在盲目地兜圈子

谢谢

编辑:实际编译器错误为

h第19行:错误:WSUHASHET不是模板

显然,粘贴到代码块中造成了混乱。下面是代码块中实际的第19行,它破坏了编译:

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

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

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

那些认为用C++来教编程是个好主意的人需要孤立。你的守卫是非法的。完整的错误(包括有问题的代码行)会有所帮助。另外,重命名你的include-guard,这样它们就不会以下划线开头。这是非常模糊的,特别是考虑到整篇文章中包含了大量的标准。我从哪里也找不到任何关于为什么在行动中包括警卫是非法的@DrewDormann我把它添加到了OP的末尾,很抱歉遗漏了it@user3216836我猜测这里的代码代表了文件WSUHashSet.h。然后我用手数到第19行。这是一个空行。这里有些可疑。