Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Std::any vs std::optional

157 views
Skip to first unread message

woodb...@gmail.com

unread,
May 11, 2018, 2:34:13 PM5/11/18
to
In this talk:
https://www.reddit.com/r/cpp/comments/8ghtom/andrei_alexandrescu_expected_the_expected_cpp/

Andrei Alexandrescu says he thinks std::optional is
the worst thing to happen to humanity. I have a lower
opinion of std::any than std::optional. If you were
banished to an island with just a few active volcanoes,
and could only have one of these, which one would it be?


Brian
Ebenezer Enterprises - Enjoying programming again.
https://github.com/Ebenezer-group/onwards

Mr Flibble

unread,
May 11, 2018, 6:47:35 PM5/11/18
to
So what? Alexandrescu has become less relevant as a C++ "authority" in
recent times. std::optional is great IMO and certainly has more utility
than std::any which I hardly ever use. Sometimes you want type erasure
but most of the time you don't.

/Flibble

--
"Suppose it’s all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I’d say, bone cancer in children? What’s that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It’s not right, it’s utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a
world that is so full of injustice and pain. That’s what I would say."

woodb...@gmail.com

unread,
May 11, 2018, 9:21:34 PM5/11/18
to
On Friday, May 11, 2018 at 5:47:35 PM UTC-5, Mr Flibble wrote:
>
> So what? Alexandrescu has become less relevant as a C++ "authority" in
> recent times.

He still has a lot of credibility.

> std::optional is great IMO

My opinion of it is between yours and Andrei's.

> and certainly has more utility
> than std::any which I hardly ever use.

Yeah.


Brian
Ebenezer Enterprises
https://github.com/Ebenezer-group/onwards


Melzzzzz

unread,
May 11, 2018, 9:44:54 PM5/11/18
to
On 2018-05-11, Mr Flibble <flibbleREM...@i42.co.uk> wrote:
> On 11/05/2018 19:34, woodb...@gmail.com wrote:
>> In this talk:
>> https://www.reddit.com/r/cpp/comments/8ghtom/andrei_alexandrescu_expected_the_expected_cpp/
>>
>> Andrei Alexandrescu says he thinks std::optional is
>> the worst thing to happen to humanity. I have a lower
>> opinion of std::any than std::optional. If you were
>> banished to an island with just a few active volcanoes,
>> and could only have one of these, which one would it be?
>>
>>
>> Brian
>> Ebenezer Enterprises - Enjoying programming again.
>> https://github.com/Ebenezer-group/onwards
>
> So what? Alexandrescu has become less relevant as a C++ "authority" in
> recent times. std::optional is great IMO and certainly has more utility
> than std::any which I hardly ever use. Sometimes you want type erasure
> but most of the time you don't.

std::any is variant, and std::optional would better be replaced by future
addition of tagged unions as in Rust eg ADT's in functional languages.
They started with decomposition of tuples, next are tagged unions ;)

>
> /Flibble
>


--
press any key to continue or any other to quit...

Sam

unread,
May 12, 2018, 11:33:46 AM5/12/18
to
woodb...@gmail.com writes:

> In this talk:
> https://www.reddit.com/r/cpp/comments/
> 8ghtom/andrei_alexandrescu_expected_the_expected_cpp/
>
> Andrei Alexandrescu says he thinks std::optional is
> the worst thing to happen to humanity. I have a lower
> opinion of std::any than std::optional. If you were
> banished to an island with just a few active volcanoes,
> and could only have one of these, which one would it be?

Based on my recent experience, I'd pick std::optional. I'm using it fairly
often, but I've yet to find much use for std::any.

Chris Vine

unread,
May 12, 2018, 7:56:55 PM5/12/18
to
C++ already has discriminated unions (aka sum types). It's called
std::variant.

Melzzzzz

unread,
May 13, 2018, 2:03:35 AM5/13/18
to
It's not built in into the language so you need std::optional,std::any
... ;)
Anyway, what is difference between std::any and std::variant?

Chris Vine

unread,
May 13, 2018, 5:36:47 AM5/13/18
to
On Sun, 13 May 2018 06:03:24 GMT
Melzzzzz <Melz...@zzzzz.com> wrote:
> On 2018-05-12, Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> > On Sat, 12 May 2018 01:44:43 GMT
> > Melzzzzz <Melz...@zzzzz.com> wrote:
[snip]
> >> std::any is variant, and std::optional would better be replaced by future
> >> addition of tagged unions as in Rust eg ADT's in functional languages.
> >> They started with decomposition of tuples, next are tagged unions ;)
> >
> > C++ already has discriminated unions (aka sum types). It's called
> > std::variant.
>
> It's not built in into the language so you need std::optional,std::any
> ... ;)
> Anyway, what is difference between std::any and std::variant?

std::variant, std::optional and std::any are all provided in the
standard library for C++17, so I am not clear what you mean that
std::variant is 'not built in into the language so you need
std::optional, std::any ...'.

std::variant is a perfectly normal sum type or discriminated union.
There is nothing much more to be said about it, except that pattern
matching on it is possible using function overloading with std::visit
but is inconvenient. std::optional is a normal Maybe or Option type.
std::any is somewhat odd. It seems to be a means of introducing
dynamic typing to C++ of the python/javascript/lisp kind, and as such I
can see it could have its uses: in particular, it does enable you to
have heterogenous C++ containers. It has a type operator which returns
a std::type_info object describing its contents, and an any_cast access
operator which throws an exception if the typeid of the contained
object is not the type requested by the cast; so to that extent it is a
safer version of void* with type introspection.

I don't use any of these because I write code which depends on C++11/14
but not C++17. From C++11 it is not that difficult to construct
discriminated (tagged) unions yourself, and I have my own home-grown
option class.

Chris

Melzzzzz

unread,
May 13, 2018, 8:03:25 AM5/13/18
to
Problem with tagged unions, that are not built into language, is in
optimisation: tag cannot be optimized out as in Haskell or Rust.
>
> Chris

Sam

unread,
May 13, 2018, 8:52:15 AM5/13/18
to
Melzzzzz writes:

> Problem with tagged unions, that are not built into language, is in
> optimisation: tag cannot be optimized out as in Haskell or Rust.

Do not underestimate a modern C++ compiler's ability to optimize away
various things.


Melzzzzz

unread,
May 13, 2018, 9:58:03 AM5/13/18
to
Struct member cannot be optimised out.

David Brown

unread,
May 13, 2018, 11:11:31 AM5/13/18
to
On 13/05/18 15:57, Melzzzzz wrote:
> On 2018-05-13, Sam <s...@email-scan.com> wrote:
>> Melzzzzz writes:
>>
>>> Problem with tagged unions, that are not built into language, is in
>>> optimisation: tag cannot be optimized out as in Haskell or Rust.
>>
>> Do not underestimate a modern C++ compiler's ability to optimize away
>> various things.
>
> Struct member cannot be optimised out.
>

Do not underestimate a modern C++ compiler's ability to optimize away
various things.

Sometimes the compiler /can/ optimise away struct members. Perhaps not
in as many situations as for some languages and tools, but compilers can
do a lot if they have the information available.

Melzzzzz

unread,
May 13, 2018, 11:23:46 AM5/13/18
to
Sometime is not same as sizeof (somestruct) == 8 instead of 16 or 12...

David Brown

unread,
May 13, 2018, 11:58:50 AM5/13/18
to
True. But languages with built-in tagged unions also need a field to
store the type if they can't optimise it away. There is little difference.

Melzzzzz

unread,
May 13, 2018, 1:55:48 PM5/13/18
to
Another case for introducing tagged unions in language:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf

James Kuyper

unread,
May 13, 2018, 3:27:17 PM5/13/18
to
On 05/13/2018 11:23 AM, Melzzzzz wrote:
> On 2018-05-13, David Brown <david...@hesbynett.no> wrote:
>> On 13/05/18 15:57, Melzzzzz wrote:
...
>>> Struct member cannot be optimised out.
>>>
>>
>> Do not underestimate a modern C++ compiler's ability to optimize away
>> various things.
>>
>> Sometimes the compiler /can/ optimise away struct members. Perhaps not
>> in as many situations as for some languages and tools, but compilers can
>> do a lot if they have the information available.
>>
> Sometime is not same as sizeof (somestruct) == 8 instead of 16 or 12...

A compiler is free to remove a struct member if it can do so without
preventing the observable behavior of your program from matching one of
the permitted behaviors allowed by the standard for your program. I
wouldn't dare assume that I could list all of the things that need to be
true in order for that to be feasible, but one of them is, for example,
that the observable behavior of your program shouldn't depend upon the
result of applying sizeof to the struct type.

woodb...@gmail.com

unread,
May 13, 2018, 5:43:13 PM5/13/18
to
Is there a way to get a compiler to tell you that it's
removing a class member? I'd like to know about it so I
can either remove the member myself or disable the
optimization.


Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net

Chris Vine

unread,
May 13, 2018, 5:47:14 PM5/13/18
to
On Sun, 13 May 2018 12:03:14 GMT
Melzzzzz <Melz...@zzzzz.com> wrote:
[snip]
> Problem with tagged unions, that are not built into language, is in
> optimisation: tag cannot be optimized out as in Haskell or Rust.

OK, I see your point.

However, pattern matching on a variant is in most cases a runtime
operation. Some form of type identification or tagging is therefore
required even in the case of the languages which have sum types built
in. Since most functional languages allocate their non-immediate
objects on a garbage collected heap, this may (and usually does) involve
allocating objects on 8 byte boundaries and using the redundant lower
bits of the address for type information ("pointer tagging").

However, there is nothing to stop C++ implementations doing the same.
If they do not, it is probably for good efficiency reasons, trading an
extra 4 bytes for more speed (say, in enabling allocation of local
objects on the stack).

David Brown

unread,
May 14, 2018, 3:09:01 AM5/14/18
to
That is up to the compiler. For gcc, I don't know of any method. Note
that the compiler can only remove a struct member if it does not affect
the working of the code - so why should it have a way to tell you "by
the way, compiler optimisations made code faster" ?

Usually you will only see field removal for local data within a function
- if the struct can "escape" in some way, it will be hard for the
compiler to track all uses and be sure a field can safely be omitted.
Within a function, the compiler will to some extent treat a local struct
as a collection of individual variables, each subject to optimisations
like dead store elimination, hoisting, caching loads, de-duplication,
strength reductions, constant propagation, etc. This is integral to
compiler optimisation, and unlikely to be controllable by a single flag.


Juha Nieminen

unread,
May 14, 2018, 5:06:29 AM5/14/18
to
Melzzzzz <Melz...@zzzzz.com> wrote:
> Anyway, what is difference between std::any and std::variant?

One major difference is that std::variant is a template class, while
std::any isn't. Something that takes an object of type std::variant
needs to hard-code its types (and their declaration order), unless
you make that something templated. Something that takes an object
of type std::any doesn't need to know what exact types it may
contain, and doesn't need to be templated.

One side effect of this is that sizeof(std::any) is always the
same (within the same compiler), while sizeof(std::variant) depends
on what types are being used.

And, of course, std::any can literally contain any type. Code taking
such an object as parameter cannot force it to contain only certain
types. (This may be a positive or a negative, depending on the situation.)

woodb...@gmail.com

unread,
May 14, 2018, 12:03:33 PM5/14/18
to
Having read some about this proposal
https://www.reddit.com/r/cpp/comments/8iw72i/p0709_r0_zerooverhead_deterministic_exceptions/

to make C++ exceptions static as opposed to dynamic, I think
std::any fit slightly better in the (now) old version of C++
that had dynamic exceptions, but std::any always was "a bridge
too far" imo.


Brian
Ebenzer Enterprises - "Whoever refreshes others will himself
be refreshed." Proverbs 11:25

http://webEbenezer.net

Jorgen Grahn

unread,
May 15, 2018, 10:07:45 AM5/15/18
to
On Sun, 2018-05-13, James Kuyper wrote:
> On 05/13/2018 11:23 AM, Melzzzzz wrote:
>> On 2018-05-13, David Brown <david...@hesbynett.no> wrote:
>>> On 13/05/18 15:57, Melzzzzz wrote:
> ...
>>>> Struct member cannot be optimised out.
>>>>
>>>
>>> Do not underestimate a modern C++ compiler's ability to optimize away
>>> various things.
>>>
>>> Sometimes the compiler /can/ optimise away struct members. Perhaps not
>>> in as many situations as for some languages and tools, but compilers can
>>> do a lot if they have the information available.
>>>
>> Sometime is not same as sizeof (somestruct) == 8 instead of 16 or 12...
>
> A compiler is free to remove a struct member if it can do so without
> preventing the observable behavior of your program from matching one of
> the permitted behaviors allowed by the standard for your program.

This sounds right.

> I wouldn't dare assume that I could list all of the things that need
> to be true in order for that to be feasible, but one of them is, for
> example, that the observable behavior of your program shouldn't
> depend upon the result of applying sizeof to the struct type.

This doesn't sound right, if you're saying sizeof has to give the same
result as if the optimization wasn't performed. Can you elaborate?

As I understand it, there's not a lot you can tell about sizeof(struct
foo). For example, surely there's no guarantee that sizeof(struct foo)
and sizeof(struct bar) are equal for two identical-looking struct types,
or that struct foo is larger than any of its members?

Not that I expect to get stellar results from such an optimization;
I don't use LTO and as soon as a struct type crosses a translation
unit border (like they often do) the optimizer has to obey the
ABI.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

James Kuyper

unread,
May 15, 2018, 10:44:03 AM5/15/18
to
On 05/15/2018 10:07 AM, Jorgen Grahn wrote:
> On Sun, 2018-05-13, James Kuyper wrote:
>> On 05/13/2018 11:23 AM, Melzzzzz wrote:
>>> On 2018-05-13, David Brown <david...@hesbynett.no> wrote:
>>>> On 13/05/18 15:57, Melzzzzz wrote:
>> ...
>>>>> Struct member cannot be optimised out.
>>>>>
>>>>
>>>> Do not underestimate a modern C++ compiler's ability to optimize away
>>>> various things.
>>>>
>>>> Sometimes the compiler /can/ optimise away struct members. Perhaps not
>>>> in as many situations as for some languages and tools, but compilers can
>>>> do a lot if they have the information available.
>>>>
>>> Sometime is not same as sizeof (somestruct) == 8 instead of 16 or 12...
>>
>> A compiler is free to remove a struct member if it can do so without
>> preventing the observable behavior of your program from matching one of
>> the permitted behaviors allowed by the standard for your program.
>
> This sounds right.
>
>> I wouldn't dare assume that I could list all of the things that need
>> to be true in order for that to be feasible, but one of them is, for
>> example, that the observable behavior of your program shouldn't
>> depend upon the result of applying sizeof to the struct type.
>
> This doesn't sound right, if you're saying sizeof has to give the same
> result as if the optimization wasn't performed. Can you elaborate?

After I posted that question, I realized that I worded the requirement
incorrectly - which is an example of why I didn't want to attempt a
complete list of the relevant requirements. Strictly conforming code
could use a combination of sizeof() and offsetof() to detect the fact
that a member has been removed, or use the same combination to write
code which won't work correctly if sizeof() and offsetof() fail to
report values correctly reflecting the removal of that member. The
presence of such code in a program could prevent it from producing
observable behavior consistent with the requirements of the C standard
if the member were removed. If so, that would prohibit the
implementation from performing such a removal.

Tim Rentsch

unread,
May 24, 2018, 5:36:04 PM5/24/18
to
woodb...@gmail.com writes:

> In this talk:
> https://www.reddit.com/r/cpp/comments/8ghtom/andrei_alexandrescu_expected_the_expected_cpp/
>
> Andrei Alexandrescu says he thinks std::optional is
> the worst thing to happen to humanity.

After looking at his talk, I think his reaction is based on
std::optional not being a good fit for his use case.
Apparently he doesn't understand that there are other
use cases for which std::optional is just what the doctor
ordered.

and...@erdani.com

unread,
May 25, 2018, 11:17:13 AM5/25/18
to
Hi everyone and thanks Brian for bringing this to my attention.

Clearly the time-honored notion of "a value or the absence thereof" has many good uses, many of which distinct from "a value of any type whatsoever" or "a value of a known set of types". So nothing wrong with having and using each of the above where applicable.

My remark about std::optional has to do with it having introduced (for the first time in std) the pointer syntax for a non-pointer value. That is questionable in and of itself - are we really after more undefined behavior in standard library APIs? -, but the worst of it is it has created a precedent - now new loosely-related artifacts (such as of course std::expected) need to integrate the pointer syntax as well, or carefully justify not doing so.

woodb...@gmail.com

unread,
May 25, 2018, 8:21:40 PM5/25/18
to
On Friday, May 25, 2018 at 10:17:13 AM UTC-5, and...@erdani.com wrote:
> Hi everyone and thanks Brian for bringing this to my attention.
>
> Clearly the time-honored notion of "a value or the absence thereof" has many good uses, many of which distinct from "a value of any type whatsoever" or "a value of a known set of types". So nothing wrong with having and using each of the above where applicable.

I agree as far as optional and variant. Perhaps there are some
good uses for std::any, but I haven't seen them yet.

>
> My remark about std::optional has to do with it having introduced (for the first time in std) the pointer syntax for a non-pointer value. That is questionable in and of itself - are we really after more undefined behavior in standard library APIs? -, but the worst of it is it has created a precedent - now new loosely-related artifacts (such as of course std::expected) need to integrate the pointer syntax as well, or carefully justify not doing so.

I hadn't thought about the precedent part.


Brian

Öö Tiib

unread,
May 26, 2018, 4:34:59 PM5/26/18
to
On Friday, 25 May 2018 18:17:13 UTC+3, and...@erdani.com wrote:
>
> My remark about std::optional has to do with it having introduced (for the
> first time in std) the pointer syntax for a non-pointer value. That is
> questionable in and of itself - are we really after more undefined behavior
> in standard library APIs? -, but the worst of it is it has created a
> precedent - now new loosely-related artifacts (such as of course
> std::expected) need to integrate the pointer syntax as well, or
> carefully justify not doing so.

The problem is precedent of making the syntax of value and
pointer usage to overlap? In practice the syntax already overlaps in
C++. The references and function pointers hide that they are pointers,
now something pretends being pointer. Are there some deeper problems?
I do not understand what you mean with undefined behaviors.

The optional is useful in places where absence of value and nullability
are needed. That can be for other reasons but errors. In such places
usual alternative is (way differently performing) unique_ptr. If
unique_ptr or optional is more optimal can be even hard to tell without
profiling. So the pointer-like operators are likely meant to simplify
refactoring and generalizing over those alternatives.

It can be that same syntax is not good interface for "expected"
value/error union. Some languages use enum syntax for declaring and
switch-case for working with tagged unions. I don't know how to achieve
that with C++.
0 new messages