Hi, I'm Nicole, I'm really
interested in the values
slash object model of C++.
'Cause I'm kinda a strange human being.
Today I'm gonna talk
about that, and hopefully
we'll all kinda understand the terrible,
awful nature of the
value and object models
of C++, but also what's so
frigging' wonderful about it.
I'm sorry for the change in title,
I did originally intend
to talk about lifetimes.
I'm not gonna talk about that today.
If you want lifetimes, go learn Rust.
(laughing)
So a little bit about me.
I'm a barista, I work
at Starbucks and most
of what I've learned is
by reading and reasoning
about the standard.
I'm really interested in
object models in all of the
in all the languages,
of all the languages.
All of the systems languages
like Rust, C, C++ and how
they differ and how they are the same.
I am a barista.
I'm so proud of this.
Like holy shit, that's frigging' awesome.
I love coffee, and especially espresso.
If you talk to me I will
talk to you for like 15 to 30
minutes about espresso machines.
If you're into that,
they're really pretty.
Definitely recommend.
And I'm been programming
since I was eight in C.
I got like a game programming book
and I learned Rust when
I was 16 and that's
kind of what brought me
started rolling downhill,
into C++.
So first of all, the rules.
I want you to ask questions.
This is not gonna be easy for
many of you who've kind of
built up your own mental
model of C++ over the years.
So just raise your hand,
I will call on you,
ask your question and I'll
repeat it and try and answer it.
And second of all, no corrections, please.
There's gonna be some
stuff that's like not
exactly right or like,
simplified slightly.
This is intended to be like
easier to understand than normal.
Just give me the space to do this please.
The basics.
What's a value?
So if you think about,
like, what is a value?
A value is kind of the platonic
ideal of a value, right?
Like if you have an int,
a value of an int is
a number between negative
two billion and positive
two billion.
If you have a care, it's
a number that represents
a character in your execution set.
If you have a string, it's
like a list of characters.
And what's an object?
And object is just a place
where you can store a value.
Right?
So if you have like on
the stack, a string,
that's just three pointers that point to
the value of the string.
Let's talk a little bit about functions.
So values semantics in C++
are super duper important.
They're kind of what C++
was originally based on.
This is how you kind
of get copy by default.
So as I understand it,
Biarni didn't really want
like reference parameters.
Or not reference parameters,
he didn't want reference,
call by reference by default.
He wanted you to be able
to have a list of stings
in your main function, pass it to foo,
and you know that foo doesn't mutate it.
Foo gets its own copy
of this list of strings.
And it was like really
different from other languages
at the time.
The other languages at the
time were like Pascal and
Fourtran, they all passed by reference.
Or you just had C, which passed by pointer
because there was no copy semantics.
And even when it did get copy semantics,
there weren't any like copy constructors.
So it just copied the bytes.
This was like a really
important change from
other languages,
and I don't know of any
other languages besides like
D and Rust that have really started to use
value semantics in this way.
Did I do that twice?
Reference semantics.
So, but C++ didn't want
you to be able to use
just pointers.
Didn't want you to just
be able to use values.
You have to allow someone in a function
that you're calling to mutate
you, or to mutate your data.
And it's the same call syntax,
but you just add a little
ampersand and then you're
literally talking about
the exact same object.
Which is really powerful.
And it works with user defined types.
It works with default types,
like it works everywhere.
I feel like there was
something else I wanted to say
about this.
Oh, but like you're
basically passing the object.
You are passing like a
handle to the object.
And if you look at it, like list here,
that is exact same type
as list of strings.
So, let me think how to word this,
if you think about it, like on the stack,
you have a vector string there.
And when you type vector
string list equals whatever,
you're saying I wanna
create a vector string
with the contents of whatever
and I'm going to say list is
a name for that vector string.
List is not the vector string itself.
And if you do like beckle type lists,
it's not a vector string,
it's a vector string L value.
So, the interesting things here is like
list of strings is the
exactly the same as list.
Which is a really powerful idea.
Now pointers.
Pointers are an interesting case.
One of the things that people
thing about pointers is like
oh they're noble references,
oh they're references
that you can do pointer arithmetic on.
But not really, like
those are fundamentally
what a pointer is.
A pointer is taking a reference,
putting it into the value
system, so you're making
a reference into a value.
And this other nullability
and arithmetic are just
mostly crough taken from C.
Like you can have non
nullable pointer types
and you just do optional
pointer in order to get
a nullable pointer type.
This is entirely reasonable
and it's like what Rust does.
Right?
And so one of the things
that you really have to understand
is that a reference is not a nonnullable,
no point arithmetic pointer.
It is actually like
it's own separate thing,
it's not a funny pointer,
it is the actual L value,
the actual object itself
and then a pointer's just
how you take that reference
and make it into a value.
That you can make a
vector of, for example.
Now let's talk about product types.
That's all easy, simple, we're
gettin' to the real meat.
What's a product type?
It's not really common in C++
to talk about product types.
But you can think of them
kind of like a struct.
So, or std::tuple, right?
Like you just have the
values of multiple types
in the same type.
And a value of a product
type is represented
by the values of each of its members.
And that makes sense,
each type's value
representation is included
in the value representation
of the product type.
And now we need to talk about
constant reference numbers
because they are terrible.
This is getting into the
terrible parts of the C++.
It doesn't fit into our
world, like we don't
have like a concept of a
constant, we just have values
and we have values that go together.
What is const?
Like you can't assign to const types, like
what is happening here?
They are strange and
let's think about this.
If you think about the
value of a product type,
it has all of the
it has all the values of the member types.
If you have a value of the product type,
it includes a const member,
and you can't mutate
the const member through
that value type, right?
Like that make sense?
Cool.
I know I'm going fast.
Like, I'm just, I'm
nervous and I'm going fast
because of it.
So, what about assignment?
Like, if you think about assignment,
what is it fundamentally?
In C++ it's the call to operator equals.
But really, what is it
when you do assignments?
Like, what do you think is
happening when you do assignment
besides like operating,
but like conceptually,
what do you do when
you're doing assignments?
Say it again, say it louder?
- [Audience Member] Copying the value.
Right, yeah, and what
do you do with the value
of the old object?
- [Audience Member] You discard it.
Right, correct.
So you copy the value of
the right hand side into
left hand side and you discard
the value of the old object.
And so the new value really
doesn't have anything
to do with the old value right?
Okay.
Const and reference members
break that completely.
And this is just like,
this is pointing out
what assignment is.
You can think of it conceptually
as destroying the old
value, putting the new
value into its place, right?
And so this is true, but
constant reference members
break that.
Const and why it's kind of bad.
A type mentioned in
basic.fundamental and basic.compound
is a CV-unqualified type.
Each type, which is
CV-unqualified complete or
incomplete object type of is
void has three corresponding
CV-qualified versions of its type.
A const-qualified version, a
volatile-qualified version,
and a const-volatile-qualified version.
The type of an object
includes the CV-qualifiers
specified in the decl-specifier-seq,
declarator, type-id,
or new-type-id when the object is created.
Okay the standard doesn't
actually give const meaning here.
It just says that it exists.
That's fine, let's see if
we can find somewhere else.
A const object is an object
of type const T or non-mutable
sub-object of such an object.
Okay?
Great.
I still don't know what I can
do with const or not const
and what's the difference,
except that any class
member declared mutable can
be modified, any attempt
to modify a const object
during its lifetime results
in undefined behavior.
Oh, well that makes sense, I guess.
Apparently it's a kind of object.
Okay, so you can have
const objects, right?
You can have like static const objects,
which are like a string or something,
or like sting, literals,
they are a const object.
This all makes sense.
But what about pointers to const?
'Cause it turns out
that you don't just have
const objects, you have pointers to const.
I'm gonna do a little beat
poetry because I like it.
And because Micheal is awesome.
A CV-decomposition of a
type T is a sequence of CV,
and P, such that T is,
CV zero, P zero, CV one,
P one, dot, dot, dot,
CV M, minus one, P n
minus, CV, N, U for any N
greater than zero.
Where each cvi is a set of
CV-qualifiers, and each P I
is a pointer to, a pointer to
member of class C I of type,
array of N I, or array
of unknown bound of.
If Pi designates an array, the
CV-qualifiers CV I plus one
on the element type are also
taken as the CV-qualifiers CV I
of the array.
Example, the type denoted by
the type-id const int star star,
has two CV-decompositions,
taking U as int and as pointer
to const int.
End example.
The n-tuple of CV-qualifiers
after the first one in
the longest CV-decomposition
of T, that is CV one, CV two,
dot, dot, dot, CV N is called the
CV-qualification signature of T.
(applause)
It is really hard to
speak to the standard,
I have to say.
I am impressed with Michael right now.
Michael Case.
A prvalue of expression of
type T I can be converted
to type T two if the following
conditions are satisfied,
where CV JI denotes the
CV-qualifiers in the
CV-qualification signature of TJ,
T one and T two are similar,
which means they're basically
the same object type,
for every I greater than
zero if const is in CV one I,
then const is in CV two I,
and similarly for volatile.
If the CV one I and the CV
two I are different, then
const is in every CV two
I for zero is less than K
is less than I.
And this is the stuff
that I'm talking about,
like this ridiculous
wording is all because
they put const on the object's type.
If you had it on the L value type,
you would just say, oh
like it's a sub type.
Like, that's, it's easy.
But no, they had to put
it on the object side.
And that means that if
you have const in star,
it's a pointer to const
it as opposed to like
say a const view of an int.
And what they're doing is
they're intending you to think
of a const and star as
a const view of an int.
But they're wording it
such that it actually works
with const and star being
a pointer to const int.
And so this all means
you can pretend a pointer
to an object, is actually a
pointer to a const object.
If basically standard
allowed type hunting.
So this is insane, obviously.
Let's ignore the complexity of
the standard wording for bit
and try and see what they
actually wanna have happen here
is they want to be able
to use const views.
Like, that seems like
an entirely reasonable
thing to do.
So back to product types.
The standard cares about const objects.
It also cares about const sub objects.
This sucks.
We can't define our own
operator equals how we'd expect
them to be defined.
When you have a const sub
object, you can't update
it without it probably
being undefined behavior.
And this is why
studlaundry was introduced.
If you're curious.
Basically how it works is,
if you have an L value,
referring to an object,
and that object has any
const reference members,
it compilers are allowed to optimize as if
those constant reference
members didn't change
even if you created a
new object in that place.
And this is a consequence of
people who are obsessed with objects.
Really it's like an
old kind of thing where
C++ is really object oriented.
Obviously.
So it has these issues.
This kind of turned
into a rant, I'm sorry.
(laughter)
I get annoyed about this stuff,
'cause it's like really silly, like
why does this exist?
So if const has these issues, what about
reference members?
And why they're cool but also bad.
What's the deal?
The standard pretty much
pretends that they don't exist.
There is no standard
behavior with regard to them.
The standard only allows
implementation to assume
they don't get rebound.
This behavior as you'll see is not great.
They're basically like pointer members,
but they don't get rebound.
And it's does anyone not
know what rebound means?
Cool.
So rebounding a reference is like changing
what it points to, basically.
So if you have, like, it's
like changing the value
of a pointer, right?
How they work is, in a product type,
how a reference works is
that it actually like,
it points to something,
it's a pointer, basically.
And the value owns an alias,
like you can think of
it as owning an alias,
but it's really the object
that owns the alias.
And it means that if you change the value,
you can't rebound any reference
members during that change.
And it completely breaks
the idea that this code
is equivalent.
Modulo exceptions, I
didn't wanna write that.
And that kinda sucks, like
I want these to be equivalent,
I want to be able to write
easy operator equals, but
they're not equivalent.
So how do we fix that?
We pretend that the wording doesn't exist.
Ignore that part of the standard.
And never use reference or const numbers.
This is kind of an
unfortunate thing because
I wanna be able to say my
thing will never change
while on the same value, right?
Like that seems like a reasonable thing,
you have auto const almost everywhere.
People do that, but then
we can fix the standard
to make references in structs
rebindable, and consts
in structs mutable.
There are apparently
people working on this.
I should have worded that differently.
What I meant is like, if
you create a new value
in that object, it should
create a new value in that
object and not like kind of
create a new value but like
pointer to the old objects
don't point to the new object.
So given this mental model,
let's look at people's
favorite data structure.
Optional .
Why it makes sense and has
one sane way of working.
The thing about optional
 is that like,
apparently some committee
members thought that it was
like a good idea to like, if it's sum
assigned to reference,
or assigned to like the
underlying reference, and
if it's none, to rebind.
Which is weird.
So let's put it into our model.
Optional T is an optional value.
Right, if you have a function
that takes an optional T,
It's taking an optional value.
Optional TN is a value that
includes an optional reference.
You can think of it as optional objects.
But, and same as with reference
members of struct types,
assigning a new value
to the optional should
you know, actually assign a new value.
Rebind semantics are the only
ones that make any sense.
Anyone have any objections to that?
Because I'm like, I'm actually curious,
are there any objections to that?
Okay.
And then in C++ 17, we got real PR values.
Oh yeah.
- [Audience Member] I actually
fully agree, the rebinds
semantics are the only
ones that make sense.
I'm just interested in knowing,
do you have any thoughts on
the same issue with tuple?
For tuple, yeah.
- [Audience Member] Because
like, people really like
the resist tie.
So, there are the opinions that I have
and then the opinions
that actually make sense
with the current wording of the standard.
I do think that tuple
operator equal should not
do what it does currently,
like I think it should
rebind those references.
That's what makes sense, right?
But they're already in the standard.
They, it's really weird
that stood tie returns
otubal of references.
Like why?
Go ahead, yeah, yeah.
Like why does stood tie return references?
They are conceptually distinct thing.
You clearly want to talk,
come up to the microphone.
(laughter)
- [Audience Member] I just
wanna say, I read a lot
of redat and tuples a lot.
And one of the common
features that people ask is
hey you should support the
references in your tuples.
And I know it's kind of
convenient, but it just breaks
everything, everything breaks down.
I think behaves as you
think it should because,
well you're not to be
binding, you're assigning
to the reference, and that
just kills everything.
So I do think that tuple
should support references,
but they should support the
one to rebind semantics.
Yeah, in general I think
that we could actually allow
equal operator overloading,
to be done by the compiler.
Like there's no reason
that operator needs to be
manually user defined, like it's
it's just useless boiler
plate at this point.
If you write your const
and constructor and your
move constructor, just have the
operator equal do that for you.
Seems entirely reasonable,
and it would do the equivalent of the code
with the exception handling.
Now I wanna talk about C++ 17.
Because we finally got real PR values.
And they're great.
Stuff like this suddenly works.
Before C++ 17, what happened was,
it would create a temporary, so,
here would create a temporary object,
and then it would const and
construct on the equals,
for no reason, it just did.
And like that's, I think
in older versions of C++
it actually like defaulted
constructed X and then
operator equaled over.
Thankfully that was fixed at some point,
but like it's exactly equivalent to
foo x.
And like every compiler has
supported this since the 90's.
Like I literally checked.
Every compiler has
supported this optimization
since the 90's.
Borland C++ 3.0 supported
this frigging' optimization
and then it just got
fixed in C++ 17 because
we actually have real values now.
Turns out showing the
error is kind of boring,
or no error is kind of boring.
But initialization if you're
doing the assignment thing,
like auto x equals y,
that changed slightly,
so basically if the initializer expression
is a PR value, like a value,
that is not related to any object,
and the CV unqualified
version of the source type
is the same,
you can read it, it's standard z wording,
it's terrible.
But it's really good.
Oh, it's called copy initialization,
that's what it's called.
So other interesting issues of PR values,
I thought I might tell you how this works,
'cause I find it interesting.
So you have foo that
takes a string ref ref
and you call it with a string literal.
What actually happens under the hood?
We create an overload set for foo.
There's only one overload so we do that.
And then we attempt to convert
this crazy type into a string.
Look at the converstions,
we have like strings,
like the copy constructor,
the move constructor,
all these weird constructors
that I didn't know
string had.
Did you know that string has
like 15, 16, constructors?
Like why does it need, anyways.
So it's there, oh look!
String char const star.
I can turn into that.
I have, what's it called?
Decay, thank you.
It has decay, and so it decays
into the char const star.
And then we call the string constructor,
how do we turn that string PR value,
like that thing, that
sting that we get returned
from the string constructor,
into a string ref ref, like foo requests,
create a temporary, crazy.
We literally turn it into this.
And like, that's great, I like it.
I don't know,
there was probably more
of this at some point.
I'm completely dead at this point.
Const PR values.
I feel like I needed to rant about this
because I've been ranting
about this all week
and they are dumb.
What do you expect this to print?
Seriously, what do you
expect this to print?
I'm curious, I wanna get,
like audience feedback.
What do you expect this to print?
- [Audience Member] What
do we think it will print
or should?
What do you think it will print?
(laughter)
What do you think it will print?
Okay, copy, raise your hand.
Nice.
Move, raise your hand.
Alright, second question,
what do you think this is gonna print?
Copy?
Move?
Alright well about half of you are wrong.
You were the only one
who was right both times.
No, no, no, you said copy
the first time, didn't you?
No this prints move
because const PR values of
int type do not exist.
You did say move?
Okay, so yes.
This prints move because they
literally just don't have
like they have a thing in the standard,
oh and this prints copy.
Because they have a section
in the standard where
they say basically, we
take PR values of type T
and if they are user defined
we do nothing with them.
And if they're standard
defined, we turn them into
non const PR values.
I don't understand this.
I actually do understand
this completely, but,
I know exactly why they do
it, and it's really silly.
Changes that I would like
to see in the standard.
And I am so under time, and
I know it, and I feel really
terrible, but I have just a few.
First, make const and reference
parameters more normal.
And I'm talking to the
standards people in the room
and I'm like you gotta do that.
'Cause they are ridiculous,
they make no sense.
Like why would you want that?
Why would you want those semantics?
No compiler optimizes for it, ugh.
Second of all, make const
PR values not a thing.
And rule that must be given coffee as a
matter of course during the meeting.
'Cause I think that's great.
Yeah.
- [Audience Member] Just to
clarify the second point,
which you are asking is
that even user defined types
basically go through this
transformation where they're
const T and just a T right?
Yeah, and that would be like.
- [Audience Member] Can you
explain, I mean it seems,
oh it's PR value so you don't.
Right, there's no actual object.
So this is like the one
place in the standard
where values are still objects.
So for the longest time, you
just kind of ignored values
and they were like,
they're technically not
objects but they are objects.
So you'll see this with
like, before C++ 17,
copy initialization was,
did that whole thing because
it actually created a
temporary object, and then
tried to move it over.
And then there was an
optimization to turn that off.
But like, it required it.
And so this is one of
those last drags of like
values are still
technically objects because
object oriented ness.
It's like despite there being no
other place in the standard where
it's a const value,
like whenever you read const standard,
it's const objects.
Because that's what const
is on, it's an object.
But this one place in the standard is like
if you wanna create a
temporary of a PR value that's
typed T const, will make
it a T const object,
for some reason.
- [Audience Member] I see.
I don't wanna throw a
curve ball at you but
could you maybe outline
what you mean by more
normal in the first bullet?
So more normal would just mean basically
you could rebind them.
If you like put a new value
into the existing object,
you would just rebind them
easily or you would change the
const value easily, and it wouldn't cause
any undefined behavior.
- [Audience Member]
Okay, if I repeat that,
just to make sure I understand.
You said you were under time and I think
this might be useful for others.
But what you're saying is basically,
if you have like
reference or const members
in an object and you kill that object,
you destroy it and then
you build a new one,
the standard should be
worded such that the compiler
sees that maybe the
reference member was rebound,
and the const object,
or the const whatever
was changed, is that correct?
Yes.
- [Audience Member] Okay, and so would,
I think that would, that
way we would not need
launder.
Say that again?
- [Audience Member] We
would not need launder.
Launder, yeah no.
No, launder is useful
for changing the V table.
I don't know why you
would do this, but you can
actually change the
dynamic type of an object.
- [Audience Member] But that's
the compiler that does that.
No, no, no, like you
can make a new object,
of a different dynamic type
with the same static type.
- [Audience Member] Okay,
thank you, thank you.
Yeah.
Any other questions, I really am,
this is my first talk and
I screwed up the timing.
(applause)
But yeah, any questions?
Go ahead.
- [Audience Member] So,
I agree that for optional
T ref the rebounds
semantics are the only ones
that make sense.
For reference members though,
what I don't really follow
is how reference members would
be different from just the
local variable reference
that assigns through,
so why are they different?
When you take the member as itself,
like if you assign to the
actual L value reference,
it wouldn't be different.
Okay?
But if you assign to the
containing struct a new value,
like if you, let's see.
Let us go to him.
Alright so if you, do, do, do, do.
Beautiful.
So if you have like a struct X
and then an int and reference member,
if you do, XX equals,
right, so that makes sense.
If you do x dot ref equals one,
it assigns through.
- [Audience Member] Okay.
However, if you do
if you do X equals Z then it changes the
binding of the reference.
- [Audience Member] That's
not the behavior today, right?
It's not, no.
- [Audience Member] So
you're saying it should
rebind in this case, I don't
understand why it would
because it's inconsistent
to me to actually assign
through in this case too.
Right, so the difference is.
If you think of it in terms of values,
so the value of the X,
holds a reference to an int, right?
- [Audience Member] You mean the object Z?
No, no, no, so this value here.
- [Audience Member] Holds
reference to Y, okay.
Holds a reference to Y, correct?
This is assigning through the reference.
However, this is changing the full value.
Of the X.
The conceptual model is
deleting the old value
at that object and putting in a new value.
(laughter)
So this X is this value,
and it's referencing Y.
This is taking the reference
Y, and assigning though it.
This is a new X value, that
you're putting into the
old object.
Right, so this is like,
this no longer has any relation to this.
You are literally assigning
into an object and
making it have a new value.
- [Audience Member] I see
what you're trying to say,
what I don't quite agree
with, I guess, is that
X in this case with the reference member,
no longer is an object that holds a value.
Right because we're
holding references now,
if it were an object that
held values, then yes
when you assign to it, it
should sever the relationship,
get a new object with
a new value, etcetera.
But it's no longer an object
that holds value somewhat.
I'm not sure that the generic
applicability of assignment
applies as soon as you
inject the reference or
cost of member for that matter.
So my argument is basically
that all structs are values.
- [Audience Member] I
mean, it's not though.
Yeah, so the struct.
Sorry.
This itself is a value.
- [Audience Member] I think
I already disagree there.
Yeah, so that's basically
it, is like all product types
are values, like you have a
value of a certain product type.
The reference member doesn't
change that, it's just
saying that a value of this
type has, or like owns,
in some sense of the
word, it owns a reference.
It is not a reference in and of itself.
- [Audience Member] One more
thing that I'll say is that,
earlier in the presentation
what I agree with is that you
said reference types are
not values and then pointer
is a reference type brought
into the value system.
Yeah.
- [Audience Member] So based off of that,
I would say that a reference
is not a value and therefore
a product type that stores
a reference is not a value
either, and so that's
kinda where I disagree
I think in terms of logic.
But I won't say anymore.
Just one last thing.
Basically, in my opinion,
a pointer should be able
to be implemented as,
like this.
Like I don't see any difference
between that and like a
pointer that's nonnullable
and you can't do pointer
arhytmatic on.
'Cause it's bringing a
reference into the value system
by being a part of a struct.
(laughing)
- [Audience Member] Because
she's not looking at me.
Alright, so, as you said, a
reference refers to an object,
right?
It's not referring to a memory
location, not a storage.
So therefore the notion
of reference is tied
to the lifetime of an object.
And you said you wouldn't
mention lifetime today,
but I was wondering if
you had a short, not too
complicated opinion about
whether C++ right now could
support lifetime qualified references.
Are we missing anything
before we could get this
in C++?
A lot of annotaions.
You could probably do it with Sal.
I've been thinking about
doing this with like
something like Sal.
You know the Microsoft things like,
in like this really ugly
thing that they have,
in the Microsoft C++ standard.
Yeah.
So you just basically
have annotations like
so for example, I don't know,
if you have a string view, X, find
then this string view
would have the lifetime
of S, basically, right?
Or like star S or something.
And I feel like you could
probably do that in C++
you just have to write
the two line.
And you have to write the annotations.
- [Audience Member] Okay, thank you.
Yeah.
- [Audience Member] So
earlier in the talk you said
how when you declare a
variable of reference type
you're not really saying
I'm declaring any object,
you're saying I'm just having a new name
for some existing object.
On that basis, the
references don't really have
values in the usual sense.
Do you think it's even
the right decision, if
you could, stuff came from scratch,
do you think it's the
right decision to allow
references members in class types at all?
I have like two competing
ideas on that, that
like fight each other in my mind.
Anyways, so, on the one hand, yeah,
it doesn't really make
a lot of sense to allow
reference members,
but on the other hand I'm like
you know, what is a
pointer other than just
a reference behind the value system?
And it makes sense to me
to like, make pointers
and references basically the same thing.
On the other hand, yeah, if
you wanna be like really,
if you wanna make a good
language you probably
don't add reference members.
If you wanna make an easy
to understand language,
you probably don't add reference members.
Okay, thanks.
- [Audience Member] So on
the topic of going to the
committee and trying
to present these idea,
we did, when variant
was being standardized,
there was a whole bunch of
talk about standardizing
variances and references.
Yeah.
- [Audience Member] And
just, there was maybe a
3,000 post thread internal
for the standards committee
regarding what the
semantics would be of the
variant references,
and I believe there were
two people who wanted
these semantics, myself included,
so I agree with you, but we could use more
people who think that way.
(laughter)
But in addition to
that, I completely agree
with your statements about the structs.
But yeah, I just, be warned that,
it's a tough battle, but on the topic,
I wanted to respond to Richard's comment.
And you evected a little bit,
but I wanted to defend this again though,
that having them as
members really is useful
and the main reason is they can come up
as independent types, so the main use case
that we were fighting for with variant,
if you have a variant of like the results,
the result types of a function call,
you want that to be able to work and if
it happens to be a reference type,
you don't want to have
to do medi programming,
which you would have to
do if you didn't have a
reference member.
Yeah and I think like
return values of reference
type make a lot of sense.
You know, and like
parameters of reference type
make a lot of sense.
And like optional T ref
makes a lot of sense.
Like you just wanna be able
to pass an optional object
you sometimes wanna pass
objects, you sometimes don't.
Like the rebind semantics like
really make sense in that case.
- [Audience Member] Anyway,
I went off on a comment,
but my actual question was
do you think it is actually
possible at this point to go
and change what references
mean in terms of structs
because when I was doing it,
it was only with respect
to library 'cause I assumed
it was possible.
I think it's actually entirely possible,
because what C++ does is it
says we don't assume anything.
Right, like so first of all,
you have the optimization
that you just turn off
and it's already turned
off in most compilers.
If not all apparently.
That you can't rebind a
reference, or you can't
change const.
So you turn that off and
then there's literally,
the only difference is
the implicitly generated
operator equals, you might
be able to have that in
more cases, even when you
have like a user defined
copy constructor, move
constructor, you could actually
implicitly define movement
assignment, copy assignment.
Which is what I really want,
'cause it's like a lot of boiler
plates, it's really silly.
- [Audience Member] I think I
tend to agree that one thing
I've encountered when arguing
stuff like this as well
is a lot of people really like
the fact that they can create
types that are immutable.
And this is one way
they do it is const and
reference data members.
Like, make it a rapper or oh.
C++ people.
(laughter)
- [Audience Member] I have
a question but actually
I wonder about the last
thing you said about mutable
objects, that was one
of the first notes I had
when you were talking
about constant members
was thinking back to a talk
that I was watching online
by one Kate Gregory who's in the audience,
and she was talking about reasons when you
would and would not
wanna have const objects
rather than const pointers to objects,
and that it's mutable if
all members are const,
and I don't know, it this
seems to kinda conflict
with that, with the mutable objects.
Is that, I don't know,
but then again neurologic
would never be assigned
into the same area right?
Right, so the thing is
what I want is like Rust stout semantics.
I hate saying it but I do.
It's like, you know, if
the actual member is const
you can't assign to it, but like
you want values to just kind of work,
and you want objects to just
be kind of storage for values.
At least in my world, I
want objects to just be
storage for values, right?
And so you can mutate the
things that are inside
a value and like reassign things that are
inside the value, or you
can reassign the whole value
and like I don't know, if
feels like const members
might not be reassignable by themselves
but you could, they're just
a value that you put over
the old value, you know?
- [Audience Member] So
what really scares me about
this is that it makes me
realize that I had assumed
that these semantics
were what I was operating
on in my C++ code, because
I would like to think about
these objects in the sense
that I can construct an object
when I say its const or it's a reference,
I'm saying I'm gonna bind
this at the beginning of the
lifetime and the constructor,
and then when its
deconstructed then it's
gone and the compiler will
help me enforce not changing
that in an inappropriate way
but then I also had this
assumption that clearly
is invalid that reassignment
of that object was
a change of lifetime
event, essentially which
it seems to be not, so my
real question is mine 28
doesn't do what I thought it
would do, what does it do?
Or does it not compile.
It doesn't compile.
Because operator equals
is not like defined
for things with reference data members.
- [Audience Member] Thank you.
And yeah, that is, it's really surprising,
like that is literally how
I wrote my copy and move
assignment operators for
like the x dot bleh x
new and x, x, z.
That's literally how I wrote
my assignment operators
for the longest time,
because I was like oh,
there's no issues with const
data members because I
don't think about them.
And it turns out that this
is incorrect for a lot
of things.
- [Audience Member] So,
if we had some way of,
some kind of operator duct, for example,
because that's the
problem, the main problem
with reference wrapper
is that you have to say
dot get every time and it's a real pain.
But if we had a proper operator dot and a
proper way of kind of delegating
things that are object,
you think we could actually
fix this in the library
by providing some kind
of a rebindable reference
type, so like a ref of T
that behaves the way we want?
And then when you say
ref of T dot something,
it just goes to the underlying object.
I mean but then you're
like basically Rust.
And you're got pointers
that have safety referencing
like you don't have to do
the minus greater than.
But then you don't have the cool
parts of references you know?
Like you can use the
same thing everywhere.
- [Audience Member]
Wouldn't that just give you
rebindable references?
The thing is like I don't
want rebindable references,
I want references that
are part of other objects,
like the value can over write, right?
- [Audience Member] Const PR values.
Yes.
- [Audience Member] This is
a question in which I asked
in committee and I don't
remember who gave me this
answer but the story as I heard it is,
you have a function caller,
tense objects of building type.
You try to assign that you get
an error, your life is good.
Yes that is the actual reason.
- [Audience Member] When
the function caller tends
an object of class type,
you try to assign to it,
it compiles, whoops.
You know how you fix this?
- [Audience Member] Well
I mean, at the time,
you fix this by making the
return type const but that's
not the right fix.
And this was before we
had reference modification
on other functions.
Yeah, so this is the correct
way of writing it today.
Everybody start writing this.
Okay?
Please.
- [Audience Member] You
cannot default it, which is?
What? Why?
- [Audience Member] Good question.
So I think, as I understand
it, that was the reason why
there are const PR
values of class types and
we should just fix that.
Yeah we should.
- [Audience Member] Also I'd
like to echo something that
Matt said, please come
to committee meetings.
(laughter)
If you can pay for it,
I'll totally come on my
barista salary.
Or barista's wage.
Any other questions?
Cool.
(applause)
