Best practice would be to accept a pair of iterators delineating the range of elements upon which Foo should operate, possibly providing convenience functions to convert ranges to iterators:
template <typename Iter>
void Foo(Iter first, Iter last) {...}
void Foo(std::vector<BaseClass>& Objects) {
Foo(begin(Objects), end(Objects));
}
void Foo(std::vector<DerivedClass>& Objects) {
Foo(begin(Objects), end(Objects));
}
// Or even a generic range adaptor,
template <typename Range>
void Foo(Range&& r) {
using std::begin; using std::end;
Foo(begin(r), end(r));
}