C++ Logo


Advanced search

Re: [std-proposals] Extend std::type_info with more information

From: Mihail Mihaylov <mihail.mihailov_at_[hidden]>
Date: Fri, 12 Apr 2024 13:52:50 +0300
OK, you convinced me (kind of). I've been working for years in a code base
that has its own any-like object, but I haven't really used `std::any`
itself. It took me a while to realize how significant it is that `any_cast`
returns a copy instead of a reference. It now seems to me that the
designers of `std/boost::any` never meant for polymorphic types to be
stored in it and the question about how to get a base out of the stored
derived value goes outside of its intended usage.

I want to emphasize that, while this usage is outside of the intended usage
of `std::any` it is definitely not outside the intended usage of any-like
types in general. To me "any" gives the connotation of the ultimate union
type, which `std::any` definitely isn't. Even though you can store anything
in it (provided it meets the constructor requirements), many types are not
handled well by it.

I'm not criticizing the design of `std::any`, apparently it was created
with certain use cases in mind and it's my mistake that I assumed a
different usage.

On Thu, Apr 11, 2024 at 7:14 PM Jason McKesson via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Why do you *need* to be able to get a base type from `any`? Or more
> specifically, why doesn't the receiver know what the derived type is?
> `any` seems plenty capable (within its narrow use cases) without this
> functionality.

If you step back from `std::any` for a bit, and look at the general
practice of working with polymorphic types, there are great advantages to
the receiver not having to know the exact type of the object. I believe
that you'd agree it's in fact the essence of polymorphism. Exceptions in
C++ are a great demonstration of this and it is no accident that they were
mentioned twice on this thread already.

I know that the standard recipe for polymorphism suggested to C++
programmers is to define a common base class. But there are cases when we
either don't have control over the types that we need to handle, or it is
impractical, and this is where union types come in. Unfortunately it turns
out that `std::any` expects to be the only source of polymorphism for the
values stored in it and destroys the polymorphism already present in the
values that users store in it.

> Note that `boost::any` lacks this functionality despite having existed
> for 20+ years. If it were really that important, wouldn't they have
> added it by now?

My guess is that the value-returning cast discourages people from using
`std::any` with polymorphic types. In other words - if it provided good
support for polymorphic values, its users would be asking for base-type
access to the value.

> If you're making a generic discriminated union where it is part of the
> interface that you can extract base class references from derived
> class contents, then you need casting infrastructure that is
> reference-oriented. That way, `any_cast<B>(a)` would work since that
> function would always return a reference.

I agree with this.

> Though personally, I argue that this functionality just isn't
> important enough to standardize. It's not that you can't imagine a
> circumstance where it's useful; it's just that this is not a
> meaningful subset of a type whose use cases are already countable on
> one hand.

But let's agree to disagree on this point.

> `any` is a very specialized type for very particular needs. And that's
> good; we *want* compile-time typing, and we should discourage more
> error-prone idioms. But there are circumstances where you need type
> erasure, and `any` provides that.

Polymorphism and subtyping are not "error-prone idioms". They are the
foundation of writing reusable code.

There are times when you need to pass an object of a known type
> through an intermediary that handles multiple such operations and
> therefore doesn't need to know what type you're passing. Callbacks,
> signal processing, things like that. In those cases, the sender and
> the receiver really ought to be on the same page as to what is being
> sent and received.
> *Precisely* what is being sent and received.

Yet again, allow me to invoke exceptions as a counterexample.

> I don't think it's good code to send an object of one type through
> this interface, but have the receiver be blind to exactly what that
> type was.

Again, you are effectively arguing against polymorphism and subtyping.

Especially if that type uses *value semantics* the way `any`
> does.

The problem is not that `any` has value semantics. The problem is that
`any_cast` copies and that was not necessary for `any` to be a value type.

In conclusion, I agree with you that asking for base access support in
`std::any` is not in line with its intended usage, but I don't agree with
your opinion that the use cases which would require this from a union type
are on the fringe or a sign of bad design.

This electronic communication and the information and any files transmitted 
with it, or attached to it, are confidential and are intended solely for 
the use of the individual or entity to whom it is addressed and may contain 
information that is confidential, legally privileged, protected by privacy 
laws, or otherwise restricted from disclosure to anyone else. If you are 
not the intended recipient or the person responsible for delivering the 
e-mail to the intended recipient, you are hereby notified that any use, 
copying, distributing, dissemination, forwarding, printing, or copying of 
this e-mail is strictly prohibited. If you received this e-mail in error, 
please return the e-mail to the sender, delete it from your computer, and 
destroy any printed copy of it.

Received on 2024-04-12 10:53:04