Why does apply_filters behave different inside and outside a loop?

There is a difference because you are not using the_post function in the first example. What this function does is it calls setup_postdata function, which sets up all the globals needed for other functions to work. You can call it manually, just at the beginning of your foreach loop, like this: setup_postdata( $post ). In addition it is possible that you would also need to call a global $post; before your foreach loop.