Why does std::basic_ios have a public constructor?
Asked Answered
H

2

16

std::basic_ios has a public constructor:

explicit basic_ios (std::basic_streambuf<CharT,Traits>* sb);

IMO, the sole reason for a class to have a public constructor is to use a standalone instance of that class in a program. If a class exists only to have other classes descend from it (as seems to be the case for basic_ios), all of the class's constructors should be protected. The constructors of std::ios_base are all protected. But, for some reason, the designers of the standard made this one constructor of basic_ios public.

basic_ios is used as a base class for several stream types, and I can't envision a use case where you'd have one that wasn't at least a basic_istream or basic_ostream. Is there one?

Hornback answered 26/11, 2019 at 16:10 Comment(0)
A
1

The other reason for a class to have a public constructor is to have this constructor signature available to construct a derived object:

struct B{
  B(int);
  protected:
  ~B();
  };

 struct A:B{
    private://no effect.
    using B::B;

    public:
    A(void*);
    };

 A a(10);

The constructor must be public in the base class because a using declaration of a base constructor does not change the accessibility of the inherited constructor.

Antiperistalsis answered 26/11, 2019 at 21:56 Comment(1)
Seems resonable, except afaik, the basic_ios ctor taking a basic_streambuf* has been public since before you could do using B::B;. I expect that old implementations just had a proxy ctor: A(int x) : B(x) {} - which works fine even if B's ctor is protected.Orangy
H
0

What I failed to notice was that std::basic_istream, std::basic_ostream and std::basic_iostream also had public constructors (each takes a std::basic_streambuf*).

This allows for a generic-programming analogue of polymorphism, in the same vein as the pimpl idiom.

That is, this way you can create a specialized streambuf type and use it in a basic_[io]stream without having to create specialized stream classes. (The functionality is limited: You can't assign a new buffer to the same stream, and you have to externally keep track of the buffer's lifetime and ownership).

The specialized basic_[io]fstream and basic_[io]stringstream each contain a full instance of the associated buffer type. This means an instance of a specialized stream type will only work with its internal buffer and not another, not even one of the same type. Using a raw basic_[io]stream is a (clunky) workaround to this.

template<class C, class TR>
class snazzy_filebuf: public std::basic_streambuf<C, TR>
{
 protected:
   typename TR::int_type overflow(TR::int_type) override;
   typename TR::int_type underflow(TR::int_type) override;
   typename TR::int_type pbackfail(TR::int_type) override;
 public:
   snazzy_filebuf();
};

.....
snazzy_filebuf<char> buf;
std::basic_ostream<char> o_s(&buf); 

o_s << "Hello, world\n";
Hornback answered 10/2, 2020 at 19:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.