При перегрузке ты можешь перегрузить функцию для разных количества и типов аргументов, но для каждого случая нужно отдельно определять функцию.
int plus(int a, int b) {return a + b;}
float plus(float a, float b) {return a + b;}
double plus(double a, double b) {return a + b;}
unsigned long long plus(unsigned long long a, unsigned long long b) {return a + b;}
// выбьет ошибку для любого другого типа, кроме int, float, double, unsigned long long
При использовании шаблонов ты определяешь шаблон только однажды, а компилятор сам сгенерирует определение стольких функций сколько потребуется.
template<typename T>
T plus(T a, T b) {return a + b;}
// Работает для любых типов, которые определяют opeator+
P.S. По сути, и перегрузка и шаблоны это и есть средства полиморфизма.