Template heap/priority-queue data structure with update-key operation I implemented the following priority-queue/heap with an update-key operation. The update key uses the map from STL, which still requires lg(n) time for lookup. However, replacing it with an unordered_map would require the client program to write hash and equal operations for each class, which is a problem in my case. 

#ifndef IZ_HEAP_H
#define IZ_HEAP_H

#include &lt;map&gt;
#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;cassert&gt;

using namespace std;

template &lt;class T&gt;
class IzHeap {
  class IzHeapItem {
    int key;
    T item;
  public:
    IzHeapItem(const int key, const T&amp; item)
      : key(key), item(item) {}
    T getItem() const {return item;}
    int getKey() const {return key;}
    void updateKey(const int newKey) {key = newKey;}
    void updateItem(const T&amp; newItem) {item = newItem;}
    IzHeapItem&amp; operator=(const IzHeapItem&amp; heapItem) {
      this-&gt;key = heapItem.getKey();
      this-&gt;item = heapItem.getItem();
      return *this;
    }
  };
  std::vector&lt;IzHeapItem&gt; heap;
  std::map&lt;T, int&gt; itemToHeapIndex;
  int leftChild(const int index) const {return index*2 + 1;}
  int rightChild(const int index) const {return index*2 + 2;}
  int parent(const int index) const {return (index - 1)/2;}
  void heapSwap(const int i, const int j) {
    /*
    IzHeapItem temp = heap[i];
    heap[i] = heap[j];
    heap[j] = temp;
    */
    std::swap(heap[i], heap[j]);

    itemToHeapIndex[getItem(i)] = i;
    itemToHeapIndex[getItem(j)] = j;
  }
  int getKey(const int index) const {return heap[index].getKey();}
  T getItem(const int index) const {return heap[index].getItem();}
protected:
  void shiftUp(const int index) {
    int largest{index}, p{parent(index)};
    if (p &gt;= 0 &amp;&amp; getKey(p) &gt; getKey(index)) {
      largest = p;
    }
    if (largest != index) {
      heapSwap(largest, index);
      shiftUp(largest);
    }
  }
  void shiftDown(const int index) {
    int smallest{index}, lc{leftChild(index)}, rc{rightChild(index)};
    if (lc &lt; heap.size() &amp;&amp; getKey(lc) &lt; getKey(smallest)) {
      smallest = lc;
    }
    if (rc &lt; heap.size() &amp;&amp; getKey(rc) &lt; getKey(smallest)) {
      smallest = rc;
    }
    if (smallest != index) {
      heapSwap(smallest, index);
      shiftDown(smallest);
    }
  }
public:
  T extract() {
    assert(heap.size() &gt; 0);
    heapSwap(0, heap.size() - 1);
    T item = heap.back().getItem();
    heap.pop_back();
    itemToHeapIndex.erase(item);
    shiftDown(0);
    return item;
  }
  T peak() {
    return heap[0];
  }
  bool empty() {
    return heap.size() == 0;
  }
  void insert(const T&amp; item, const int key) {
    heap.push_back(IzHeapItem(key, item));
    itemToHeapIndex[item] = key;
    shiftUp(heap.size()-1);
  }
  void updateKey(const T&amp; item, const int newKey) {
    assert(itemToHeapIndex.count(item) &gt; 0);
    int index = itemToHeapIndex[item];
    int oldKey = getKey(index);
    heap[index].updateKey(newKey);
    if (newKey &gt; oldKey) {
      shiftDown(index);
    }
    else {
      shiftUp(index);
    }
  }
  void heapify(const std::vector&lt;T&gt;&amp; items, const std::vector&lt;int&gt;&amp; keys) {
    assert(items.size() == keys.size());
    for (int i = 0; i &lt; items.size(); ++i) {
      heap.push_back(IzHeapItem(keys[i], items[i]));
      itemToHeapIndex[items[i]] = i;
    }
    for (int i = heap.size()/2 - 1; i &gt;=0; --i) {
      shiftDown(i);
    }
  }
  friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, const IzHeap&lt;T&gt;&amp; heap) {
    for (auto&amp; hi : heap.heap) {
      out &lt;&lt; "(" &lt;&lt; hi.getKey() &lt;&lt; "-&gt;" &lt;&lt; hi.getItem() &lt;&lt; "), ";
    }
    return out;
  }
};

#endif


Please comment on any improvements that can be done on this code. This is my first time posting on code review, so please let me know of any norms I should follow.
peak() should be returning a const reference. same goes for getItem().
Extract() should not return anything. Having two functions to read the head just begs for someone to accidentally call Extract() when they meant peak().
Why is key forced to be an int? It could easily be a separate template parameter.
heap.push_back(IzHeapItem(keys[i], items[i])); should be heap.emplace_back(keys[i], items[i]); instead.
you should do a heap.reserve() at the top of heapify()
Heapifying a vector&lt;> containing the same item twice would break this pretty badly. You should guard against this.
Unless you intend to call heapify() multiple times in a row, that should just be a constructor. Otherwise, I would rename that function addToHeap()
I prefer to avoid using the [] operator on maps in general unless I specifically want a "insert-or-replace" logic. There's a bunch of logic to handle creating a new instance, which assumes the value type of the map has a default constructor.


so:

itemToHeapIndex[items[i]] = i;


becomes 

itemToHeapIndex.emplace(items[i], i);


and 

assert(itemToHeapIndex.count(item) &gt; 0);
int index = itemToHeapIndex[item];


becomes

auto found = itemToHeapIndex.find(item);
assert(found != itemToHeapIndex.end()); // Bonus! no additional lookup in debug
int index = found-&gt;second;


Super minor detail:

The peak() function name made me squint. Traditionally, peek() means "see the next value that will come out of the datastructure", which is what happens here. This is not really a problem you have to fix, more of an observation. I would personally have used top() and pop() to match established standards.