I would suggest that there may be a preference for a particular type of operator overload for optimization purposes.
Well no. At the end of the day, both of them form as function calls. There is not even a clear sense of overloading the resolution itself. As the standard dictates in [over.match], paragraphs 2 and 6 :
If any operand has a type that is a class or an enumeration, a user-defined operator function may be declared that implements this operator or a user-defined conversion may be necessary to convert the operand to a type that is suitable for the built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator to call the operator.
The set of candidate functions for overload resolution is a combination of candidate candidates, non-member candidates, and built-in candidates. The argument list contains all operands of the operator. The best function from a set of candidate functions selected according to [over.match.viable] and [over.match.best].
All of these operator overloads are resolved together. The only semantic difference is that a class derived from ostream can choose to hide certain member overloads. This is done according to how overloading works in a derived class. Only explicit overloads will apply. Unlike these members, overloads of a free function will always be involved in overload resolution, even for classes that are derived from ostream .
Since a derived class must be converted to ostream& in order to select an overload of an arbitrary function, its own implicit conversion sequence needs to be ranked. And this can cause ambiguity if all overloads are free functions.
Thus, the consideration may well be to separate types that can cause ambiguity (pointers and arithmetic types) from useful types that we can always have available (pointers to C-lines and single characters). And allow hiding the โless usefulโ to avoid these ambiguities.
As pointed out by WF ostream actually basic_ostream<char> . Free functions just happen for data that only needs streaming. Characters or strings in the native "alphabet" of streams. So for basic_ostream<wchar_t> these free functions will take wchar_t and wchar_t* . It is possible that simple streaming does not require access to the private stream sector.
Other overloads are for data that requires serialization before streaming. Since this serialization is closely related to the internal state of the threads, it makes sense to make these overloads members.