Trying for an efficient and readable heap-sort implementation
Sort your includes. That way, you can keep track even if there are more of them.

Writing a test-program is a good idea. Though print the seed-value, and allow overriding from the command-line for reproducibility.
In line with that, add a method to test whether a range is ordered, print that result and use it for the exit-code too.

I would expect a function named print_vector() to, you know, print a vector. Not an iterator-range from a vector. Also, encoding the type of an argument in the function-name hurts usability, especially in generic code.

fill_vector() is a curious interface. I would expect get_random_data() which returns the vector.

Know your operators. ++i, i &lt;= num_of_elems is equivalent to ++i &lt;= num_elements.
Anyway, that should be a for-loop, or you could omit i and just count the argument down to zero.

Kudos for using constexpr to avoid preprocessor-cnstants where not needed. Still, ALL_CAPS_AND_UNDERSCORES identifiers are generally reserved for preprocessor-macros. They warn/assure everyone that preprocessor-rules apply. Fix the naming too.

The C++ headers &lt;cxxx&gt; modelled on the C headers &lt;xxx.h&gt; only guarantee to put their symbols into ::std. Don't assume they are also in the global namespace.

max_heap() will often try to create pointers far beyond the passed range. Creating such a pointer invokes undefined behavior.
For simple and correct code, better use indices.