Why can't I inject expressions in Compile using (only) With

Compile is considered a scoping construct by the outer With, and its bindings are protected. This will work:

With[{args = {{x, _Real}}}, Hold[Compile @@ Hold[args, x + 2]]] 

or this (if you don't want to keep Apply in code):

Unevaluated[ 
  With[{args = {{x, _Real}}}, Hold[Compile[args, x + 2]]]
] /. Compile -> $compile /. $compile -> Compile 

One relevant link is this. If you don't want to care about inner scoping constructs, use replacement rules instead of With - the injector pattern at your service:

Hold @ Compile[args, x + 2] /. Unevaluated[args ->   {{x, _Real}}]

For the Trott-Strzebonski part, it works because there, With is a secondary device serving essentially replacement rules. There, you could directly use RuleCondition instead of With. So my point is, for Trott-Strzebonski trick, the essential part is that one uses replacement rules. And rules don't care about inner scoping constructs.

It has been noticed in comments and in the edit of the original question, that not all scoping constructs are treated in the same way, and in particular for Function (with named arguments) and Compile, the entire declaration is protected, while for With and Module the protection happens on the level of individual variables (in terms of name collisions).