Then you would end up with two layers of buffering
I see no reason why you would need two layers of buffering. The streambuf
can rely upon the stdio layer's buffering; it doesn't have to provide its
own.
and considering the similarity between C's fopen and constructing an
fstream, gain very little.
fopen() and freopen() are typically not the only way to get a FILE*, even
if they're the only mechanisms specified by the standard.
It's one thing to only specify the most portable mechanisms for creating a
stdio stream, but another thing to assume that no other mechanisms exist,
effectively treating stdio as a hermetically-sealed unit which isn't to be
extended.
As it stands, any implementation wishing to extend the mechanisms for
creating streams has to do so twice: once for cstdio, once for ios.
It also wouldn't solve one of the most common (at least for me)
requirements: a stream over an existing file descriptor that isn't a
regular file.
fdopen() doesn't care whether the descriptor refers to a regular file.
fdopen() isn't portable, but then neither are descriptors.
And whatever's there for stdin (etc) and cin (etc) already has to deal
with whatever the standard descriptors are associated with, whether a
file, tty, pipe, socket, etc.