Ich denke die Zsh kombiniert einfach verschiedene gebräuchliche Syntax-Formen, um mit anderen Shell kompatibel zu sein. Es gibt aber auch einen Unterschied zwischen {} und (). Zumindest meiner Beobachtung nach: Die runden Klammern spielen eher eine Rolle beim Globbing um Regex-Style Globs zu schreiben. Die Geschweiften klammern Spielen eher eine Rolle bei der Substitution.
Mal ein Beispiel:
touch {first,second}.txt
Erzeugt die Dateien first.xt und second.txt, weil die geschweiften Klammern von der Shell entsprechend expandiert werden.
touch (first|second).txt
Erzeugt keine Dateien, weil die Shell das nur als Globbing versteht. Wenn die Dateien nicht vorhanden sind, wird auch nicht expandiert.
Ist zumindest bei meiner ZSH-Config so.
Edit:
Konkret zur Lösung:
ls [^ui]*.{h,cpp}
wird zur
ls [^ui]*.h [^ui]*.cpp
expandiert und dann erst wird der Globbing angewendet. Das ist natürlich nur suboptimal, falls es zum Beispiel keine *.h oder *.cpp gibt, die auf das Globbing passen, gibt es bei der Bash glaube ich einen Fehler. Die Schreibweise mit () ist da robuster, weil das nur ein Globbing-Ausdruck ist, aber ich weiß nicht, ob die Bash das unterstützt (benutze sie nie).