C++ Merge Sort Implementation - Pt2
Well, the first point is that you are still allocating memory for every single merge. That's expensive.

Second, you are always copying all elements. That's inefficient too.

Third, prefer to move instead of copying. Some types are move-only, others just far more efficiently moved.

Try something along these lines:

#include &lt;iterator&gt;
#include &lt;algorithm&gt;
#include &lt;utility&gt;
#include &lt;memory&gt;

template&lt;class It&gt;
void MergeSortMerge(It begin, It mid,
    typename std::iterator_traits&lt;It&gt;::value_type* aux)
{
    while(begin != mid &amp;&amp; !(*mid &lt; *begin))
        ++begin;
    if(begin != mid)
        std::move(begin, mid, aux);
    for(; begin != mid; ++begin) {
        if(*mid &lt; *aux) {
            *begin = std::move(*mid);
            ++mid;
        } else {
            *begin = std::move(*aux);
            ++aux;
        }
    }
}

template&lt;class It&gt;
void MergeSortImpl(It begin, It end,
    typename std::iterator_traits&lt;It&gt;::value_type* aux)
{
    const auto size = std::distance(begin, end);
    if(size &lt; 2)
        return;
    const auto mid = std::next(begin, size / 2);
    MergeSortImpl(begin, mid, aux);
    MergeSortImpl(mid, end, aux);
    MergeSortMerge(begin, mid, aux);
}

template&lt;class It&gt;
void MergeSort(It begin, It end) {
    const auto size = std::distance(begin, end);
    if(size &lt; 2)
        return;
    using T = typename std::iterator_traits&lt;It&gt;::value_type;
    std::unique_ptr&lt;T[]&gt; aux = new T[size / 2];
    MergeSortImpl(begin, end, aux.get());
}


The one style-comment I have is that streaming a single character as a char is marginally more efficient than as a length-1-string.

You only use arrEnd - 1 in two places, where you could use arrBegin + 1 instead. Changing that allows the use of mutable forward-iterators, whereas before you required mutable bidirectional iterators.

Adressing your extra questions:


It's better to move than copy where you can.
Your algorithm is still not stable, change if (*(i1) &lt; *(i2)) to if (!(*i2 &lt; *i1)).
You have fewer allocations than before, one for every merge instead of one for every recursive call to sort. That's still far more than the single one needed though.
That's the beginning. Next, you have to really think about how to most generically use the type / expressions affected, and to minimize the interface used.

std::iterator_traits, std::distance and std::advance are your friend there, and it wouldn't be amiss to only use &lt; and == out of all relational operators, as those are the ones used by most generic code (especially all of the standard library).
Nearly. Use an extra-level of indirection, MergeSort is the public API doing the allocating and calling the recursive MergeSortImpl which calls MergeSortMerge.