Quicksort - follow-up
Sorting an empty array is perfectly valid.

if (c.size() == 0) {
    throw std::out_of_range("no size");
}


The result id an empty array. An exception is not the correct way to go.

OK optimize. An array of size 0 or 1 is already sorted.

if (c.size() == 1) {  // I would make this c.size() &lt;= 1 (as I would remove the exception)
    return;
}


Special casing the std::end(c) just looks wrong.

   while (i+1 != std::end(c) &amp;&amp; *(++i) &lt; p) {


I would say that if i+1 == high would be a better test. Also adding the ++ and * operators into the expression makes it really hard to read. I would go with:

    while ((i &lt; high) &amp;&amp; (*i &lt; p)) { ++i;}


The decrementing the j pointer should look exactly the same as the increment.

This is not tested (so may have bugs).
But this is how I would write partition.

// Note: this function require std::distance(low, high) &gt;= 3
// Note: it is called from sort when std::distance(low, high) &gt; 15
template&lt;typename I&gt;
std::pair&lt;I,I&gt; partition(I low, I high) {
    --high;
    I   pivot   = high;

    I   highBot = low;
    I   lowTop  = high;
    --lowTop;
    while (highBot &lt; lowTop) {
        while ((highBot &lt; high)  &amp;&amp; (*highBot &lt;= *pivot))   { ++highBot;}
        while ((lowTop &gt;= low)   &amp;&amp; (*pivot &lt; *lowTop))     { --lowTop;}
        if (highBot &lt; lowTop) {
            std::swap(*highBot, *lowTop);
        }
    }
    // At this point highBot and lowTop have just passed each other.
    // Thus highBot points to the first element greater than *pivot
    // and lowTop points to the last element smaller or equal to *pivot
    swap(*highBot, *pivot);

    // Now: highBot points to the pivot value and the value that was
    // at highBot is in the top end of the high range.

    // We want to to remove pivot value from the range of elements to be sorted.
    // so we want to sort the two ranges likes this:
    //
    //      [low..lowTop+1)
    //      highBot                 The pivot point removed from further sorting.
    //                              Note: lopTop+1 == highBot
    //      [highBot+1..high)
    //

    // Small optimization
    // If the value at lowTop is also equal to the pivot value
    // Then we can remove it from further sorting.
    for(; (lowTop &gt;= low) &amp;&amp; (*p == *lowTop); --lowTop) {}

    return std::make_pair(lowTop+1, highBot+1);
}


Usage:

std::pair&lt;I,I&gt; part = partition(low, high);
sort(c, low, part.first);
sort(c, part.second, high);