this post was submitted on 19 Apr 2024
18 points (100.0% liked)

C++

1778 readers
1 users here now

The center for all discussion and news regarding C++.

Rules

founded 1 year ago
MODERATORS
 

In c++17, std::any was added to t he standard library. Boost had their own version of "any" for quite some time before that.

I've been trying to think of a case where std::any is the best solution, and I honestly can't think of one. std::any can hold a variable of any type at runtime, which seems incredibly useful until you consider that at some point, you will need to actually use the data in std::any. This is accomplished by calling std::any_cast with a template argument that corresponds to the correct type held in the std::any object.

That means that although std::any can hold a type of any object, the list of valid objects must be known at the point that the variable is any_cast out of the std::any object. While the list of types that can be assigned to the object is unlimited, the list of types that can be extracted from the object is still finite.

That being said, why not just use a std::variant that can hold all the possible types that could be any_cast out of the object? Set a type alias for the std::variant, and there is no more boilerplate code than you would have otherwise. As an added benefit, you ensure type safety.

top 4 comments
sorted by: hot top controversial new old
[–] [email protected] 22 points 7 months ago* (last edited 7 months ago) (1 children)

I think of std::any as a void* that retains type info.

A typical use case for void* is user data in callback functions. If you’re writing a library that offers callbacks to client code, you may want to provide a way for the user to pass along their own data when registering a callback. Then when calling it, you return that data unmodified*. The library doesn’t know nor care what this user data is. Since the days of K&R C, this has been done with void*.

But void* erases the type. The library may not care about the type, but the client code does. The only way to get the original type from a void* is an unsafe cast. std::any mitigate this.

*edit: unmodified, not modified!

[–] [email protected] 3 points 7 months ago

You bring up a great point with callback functions. I have written plenty of code that uses a 3rd-party library that expects a void* in a callback, and code I write myself is almost certainly never going to be used by anyone other than me (so I already know what types are valid). If library authors would start using std::any instead of void*, that would certainly improve things significantly. void* is really one of the very few C-style language features that I still use, and only because of necessity.

[–] [email protected] 7 points 7 months ago
[–] [email protected] 2 points 7 months ago* (last edited 7 months ago)

I remember once researching when to use variant and any, and coming up with https://stackoverflow.com/questions/56303939/c-stdvariant-vs-stdany. The naïve summary being:

any is a dressed-up void*. variant is a dressed-up union.

So you'd use std::any for similar reasons to void* (that other commenters already mentioned) while getting some advantages. In that sense it's kinda similar to using a std::span for pointer arithmetic instead of actual C-style pointer arithmetic, it makes a necessary evil safer to do.