Хорошая причина может быть в том, что
void foo(bar, xyzzy = 0);
похоже на пару перегрузок.
void foo(bar b) { foo(b, 0); }
foo(bar, xyzzy);
Более того, иногда выгодно преобразовать его в такой:
void foo(bar b) { /* something other than foo(b, 0); */ }
foo(bar, xyzzy);
Даже когда она написана как одна, она по-прежнему похожа на две функции в одной, ни одна из которых не является «предпочтительной» в каком-либо смысле. Вы вызываете функцию с одним аргументом; один с двумя аргументами - фактически другая функция. Нотация аргумента по умолчанию просто объединяет их в одну.
Если бы перегрузка имела требуемое вами поведение, то для согласованности она работала бы в случае, когда шаблон разделен на два определения. Это не имеет смысла, потому что тогда вычитание будет вытягивать типы из несвязанной функции, которая не вызывается! И если он не будет реализован, это будет означать, что перегрузка списков параметров другой длины становится «гражданином второго сорта» по сравнению с «аргументом по умолчанию».
Хорошо, если разница между перегрузками и настройками по умолчанию полностью скрыта для клиента.