- Overall, LGTM.

- A protection against negative indices being passed to `parent` doesn't worth the effort. `parent` is a private method, so you are in control of the indices at all times. A strong indication that the protection is not needed is the fact that `insert` doesn't bother to test the return value for validity.

- Along the same line, `left()` and `right()` returning `-1` doesn't look like a good idea. Effectively, you test the same condition twice: `((2 * i) + 1) < heap.size() - 1` in `left`, and `l != -1` in `heapify`. 

- Notice that anytime `right` is valid, `left` is also valid. That allows a certain optimization (see below).

- C is very good in recognizing tail recursion and optimizing it out. I strongly recommend to do it explicitly anyway.

- Combining the three bullets above, consider

        void heapify(int i)
        {
            while ((r = right(i)) < heap.size()) {
                follow your swapping logic
            }

            if ((l = left(i)) < heap_size()) { // No need to loop - it may only happen once!
                if (heap[l] < heap[i]) {
                    std::swap(heap[i], heap[l]);
                }
            }
        }

- `MinHeap::heapify` is a misnomer, and somewhat confusing. Usually `heapify` refers to the process of turning an array into a heap. Your method is normally called `sift_down`.

- Too many comments to my taste.