We have seen that std::ostream_iterator is really just a syntax hack, which kind of squeezes the act of printing into the form and syntax of an iterator. Incrementing such an iterator does nothing. Dereferencing it only returns us a proxy object whose assignment operator forwards its argument to an output stream.
Output stream iterators that are specialized on a type T (as in ostream_iterator<T>) work with all types for which an ostream& operator<<(ostream&, const T&) implementation is provided.
ostream_iterator always tries to call operator<< for the type it was specialized for, via its template parameter. It will try to implicitly convert types if the same is allowed. When we iterate over a range of A-typed items but we copy those items over to output_iterator<B> instances, this will work if A is implicitly convertible to B. We did exactly the same thing with struct bork: a bork instance is implicitly convertible from an integer value. That is why it was so easy to throw a lot of "bork!" strings onto the user shell.
If implicit conversion is not possible, we can do that ourselves, using std::transform, which is what we did in combination with the word_num function.