The normal operation of apache/mod_rewrite doesn’t work like this, as it seems to turn the plus signs into spaces.
I don’t think that’s quite what’s happening. Apache is decoding the %2Bs to +s in the path part since + is a valid character there. It does this before letting mod_rewrite look at the request.
So then mod_rewrite changes your request ‘/tag/c++’ to ‘script.php?tag=c++’. But in a query string component in the application/x-www-form-encoded format, the escaping rules are very slightly different to those that apply in path parts. In particular, ‘+’ is a shorthand for space (which could just as well be encoded as ‘%20’, but this is an old behaviour we’ll never be able to change now).
So PHP’s form-reading code receives the ‘c++’ and dumps it in your _GET as C-space-space.
Looks like the way around this is to use the rewriteflag ‘B’. See http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags – curiously it uses more or less the same example!
RewriteRule ^tag/(.*)$ /script.php?tag=$1 [B]