Date: Wed, 22 Jan 2025 17:38:23 +0100
I think that this will be quite possible to do with the help of
reflections. Seeing the Herb Sutter talk they mention the "instrumentation
application" where you essentially takes all the functions from one class,
expose them in another class but with added logic around each function. So
a more powerful way to achieve what you want seems to be on the way already.
On Wed, Jan 22, 2025, 17:15 Arthur O'Dwyer via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On Wed, Jan 22, 2025 at 10:48 AM Federico Kircheis via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> On 22 January 2025 15:29:38 UTC, Dennis Gusev via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>> >If you look up "inheriting from STL containers", the top results from SO
>> >and reddit all mention it being a bad practice since standard containers
>> >aren't designed to be inherited from.
>> >
>> >Specifically, they dont have virtual destructors so there's a risk of UB
>> >from deleting through pointer to base.
>>
>
> That's not the reason. Even "slicing" isn't the biggest reason. The
> biggest reason IMO is that "STL types are like a box of chocolates: you
> never know what you're going to get." The API of a standard library type
> tends to change in subtle ways with every release, not to mention varying
> in subtle ways across different vendors. There's no sensible way to
> "extend" an interface that may change out from under you.
>
> https://quuxplusone.github.io/blog/2018/12/11/dont-inherit-from-std-types/
>
>> If you think you *do* know “what you get” when your type T inherits from
>> std::vector: quick, does your type T have a member function ==, and what
>> does it do? Does it have emplace_back, and if so, what is emplace_back’s
>> return type? Does it have CTAD deduction guides? And so on. It’s not that
>> these questions don’t *have* answers; it’s that *you* don’t know the
>> answers (and neither do your coworkers).
>> [...] If you (or your project, or your company) didn’t write class Foo,
>> then class Foo should not be granted control over the API of your own
>> class. And that’s what you’re doing when you inherit from a base class:
>> you’re granting that class control over your API.
>
>
> What you *can* safely do is "composition, not inheritance." Take the STL
> type and make it a *member*, not a base. Re-expose exactly the API you
> care about, through an overload set of member functions that you control.
>
>
>> Protected and public inheritance.
>> Are they not enough?
>>
>
> Protected and private inheritance allow you to somewhat-disable the
> derived-to-base conversion, but:
> (1) they don't actually take it away, they just make it *inaccessible*,
> which can have unwanted effects on overload resolution;
> (2) they also make-inaccessible all of the member functions, member types,
> etc., of the base class. My understanding is that Dennis wants to keep the
> members accessible, so that you can do e.g. `sonOfVector.size()` and
> `sonOfVector[i]` but forbid `vector<int>(sonOfVector)`.
>
> This goal will feel much more impossible when you realize that C++23's
> "explicit object parameter" functions allow us to write not only
> vector(const vector& other); // Dennis wants to prevent passing a
> SonOfVector to that const vector& parameter
> but also
> size_t size(this const vector& self); // But Dennis *doesn't* want
> to prevent passing a SonOfVector to *that* const vector& parameter!
> This is related to the perennial impossibility of "strong typedefs."
> <https://quuxplusone.github.io/blog/2018/06/12/perennial-impossibilities/#strong-typedefs>
>
> –Arthur
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
reflections. Seeing the Herb Sutter talk they mention the "instrumentation
application" where you essentially takes all the functions from one class,
expose them in another class but with added logic around each function. So
a more powerful way to achieve what you want seems to be on the way already.
On Wed, Jan 22, 2025, 17:15 Arthur O'Dwyer via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On Wed, Jan 22, 2025 at 10:48 AM Federico Kircheis via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> On 22 January 2025 15:29:38 UTC, Dennis Gusev via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>> >If you look up "inheriting from STL containers", the top results from SO
>> >and reddit all mention it being a bad practice since standard containers
>> >aren't designed to be inherited from.
>> >
>> >Specifically, they dont have virtual destructors so there's a risk of UB
>> >from deleting through pointer to base.
>>
>
> That's not the reason. Even "slicing" isn't the biggest reason. The
> biggest reason IMO is that "STL types are like a box of chocolates: you
> never know what you're going to get." The API of a standard library type
> tends to change in subtle ways with every release, not to mention varying
> in subtle ways across different vendors. There's no sensible way to
> "extend" an interface that may change out from under you.
>
> https://quuxplusone.github.io/blog/2018/12/11/dont-inherit-from-std-types/
>
>> If you think you *do* know “what you get” when your type T inherits from
>> std::vector: quick, does your type T have a member function ==, and what
>> does it do? Does it have emplace_back, and if so, what is emplace_back’s
>> return type? Does it have CTAD deduction guides? And so on. It’s not that
>> these questions don’t *have* answers; it’s that *you* don’t know the
>> answers (and neither do your coworkers).
>> [...] If you (or your project, or your company) didn’t write class Foo,
>> then class Foo should not be granted control over the API of your own
>> class. And that’s what you’re doing when you inherit from a base class:
>> you’re granting that class control over your API.
>
>
> What you *can* safely do is "composition, not inheritance." Take the STL
> type and make it a *member*, not a base. Re-expose exactly the API you
> care about, through an overload set of member functions that you control.
>
>
>> Protected and public inheritance.
>> Are they not enough?
>>
>
> Protected and private inheritance allow you to somewhat-disable the
> derived-to-base conversion, but:
> (1) they don't actually take it away, they just make it *inaccessible*,
> which can have unwanted effects on overload resolution;
> (2) they also make-inaccessible all of the member functions, member types,
> etc., of the base class. My understanding is that Dennis wants to keep the
> members accessible, so that you can do e.g. `sonOfVector.size()` and
> `sonOfVector[i]` but forbid `vector<int>(sonOfVector)`.
>
> This goal will feel much more impossible when you realize that C++23's
> "explicit object parameter" functions allow us to write not only
> vector(const vector& other); // Dennis wants to prevent passing a
> SonOfVector to that const vector& parameter
> but also
> size_t size(this const vector& self); // But Dennis *doesn't* want
> to prevent passing a SonOfVector to *that* const vector& parameter!
> This is related to the perennial impossibility of "strong typedefs."
> <https://quuxplusone.github.io/blog/2018/06/12/perennial-impossibilities/#strong-typedefs>
>
> –Arthur
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2025-01-22 16:38:37