Trying for an efficient and readable merge-sort implementation
The single most important advantage of merge sort is its stability. The elements compared equal retain their original order. Your code loses stability. If l[i] compares equal to r[j], the latter is merged first.

Your code assumes closed ranges (that is, end is a valid index). That leads to some unpleasant fiddling with indices. Semi-open ranges (that is, end is the first invalid index) are much cleaner to deal with.

size_t middle = (start+end)/2; is vulnerable to arithmetic overflow. Consider
  size_t middle = start + (end - start)/2;


Avoid VLAs. They are vulnerable to stack overflow, and besides, they are not supported by c++ standard.

No naked loops, please. Every loop implements an algorithm, and therefore deserves a name. The before and after the actual merge in fact implement copy.

I don't see anything resembling c++.  The entire code is plain old c.