an ifstream object does not hold the entire file, it only holds a few kb window into the file (the buffer), which slides forward as you keep hitting the end with ifstream::read() (conceptually: in practice, for a non-converting byte stream such as this, read() will be usually translated to a single OS read call or even a memory map)
If you want to avoid having to load the entire file into a vector, you could memory-map the file (using your OS API or boost.iostreams) and pass the pointer to the file's contents directly to foo(). Otherwise, a copy will have to happen, one way or another.
PS: looked at the execution of ifstream::read() for a large binary file on a few compilers out of curiosity:
xlc called read() in a loop (reading 4k chunks)
sun/libCstd called lseek/read() in a loop (reading 8K chunks)
sun/stlport4 called lseek/mmap/munmap in a loop (mapping 1M chunks)
gcc called read() for the whole thing (good job implementing that xsgetn)