I think there's a technical reason / limitation that $N is used internally during regex. It might be avoidable (read: it's most likely avoidable), but I'm not too sure it's worth avoiding. It might involve a lot more code complexity and introduce more bugs, simply to add a small extra convenience. I also don't see what's so bad about the "workarounds".
Without seeing any actual code, we are just throwing wild guesses. However mIRC does use $N (and $+ although we can't really touch that one) internally piping it through the interpreter to perform the substations. This was probably the easiest way to accomplish this. It was likely avoidable (perhaps still is).
I actually see this as a feature, not a bug. Other languages like Perl put the regex match inside $N, although of course $N is dedicated to matches in those languages. In some sense, you can see $N inside regex as a tokenized list of the resulting matches. Of course this means wiping out the previous $N, but the behaviour is similar to $v1/$v2 inside nested ifs (or even more appropriately, $1- inside of $findfile()). You could see the replace part of $regsubex as a nested tokenization context for $N.
I have to strongly disagree with you on that one, it makes simple operations become overly convoluted. A simple `return $regsubex(...., $1 foobar $2 foobar $3)' for example has to be transformed into `var %a = $1, %b = $2, %c = $3 | $regsubex(...., %a foobar %c foobar %c)' for no real reason other than the fact $N are being forcefully edited without our control (or perhaps that's what you call a feature). Most scripts out there resort to some variation of that, mostly by storing the entire replacement in one variable and using that variable as the substitution. This should absolutely not be necessary, unless of course: it's a feature!
Languages that use $N replacement have that as a dedicated feature, they don't alter values of thing unwillingly – ie they aid, not hinder usability.
Don't compare apples to oranges, they don't behave the same. $N is nothing like $v1/$v2. They don't change values during the comparisons itself, only after, which is exactly what these identifiers should be doing. ((1 == 2) || ($v2 == 2)), we know $v2 is 2 and we also know it won't magically change to something we don't want it to.
Lastly, using evaluation brackets alone to place the content of $N before the substation is completely out of the question, as the $regsubex will evaluate them again allowing for mSL injections to take place. We *could* work around the 'work around' by telling mIRC to not evaluate the content of the identifiers we previously evaluated via some hackey crap like $regsubex(foo, /(.)/, $( [ $1 ] ,0) ). Of course this still isn't bulletproof as someone could easily enter something along the lines of ' $ip)(' and be able to evade our trick.