When I added the drawbacks to the Codex, I was mainly thinking of using the ‘request’ filter as an alternative to query_posts().
That filter is only run for the main query, so that solves the problem with ‘pre_get_posts’, which fires for every query.
The downside is that you don’t have access to query flags like is_single() etc.
Here’s one way you could get access to them, without actually doing the SQL queries:
function alter_the_query( $request ) {
$dummy_query = new WP_Query(); // the query isn't run if we don't pass any query vars
$dummy_query->parse_query( $request );
// this is the actual manipulation; do whatever you need here
if ( $dummy_query->is_home() )
$request['category_name'] = 'news';
return $request;
}
add_filter( 'request', 'alter_the_query' );