Welcome everybody.
Welcome to this afternoon's session.
My name is Klaus.
I will talk to you about member functions
and free functions.
I believe you've come
here to find definitive
answer to this question.
What should I use free
function or member function?
What's better?
Spoil alert it's kind of in
the title, but you want proof.
Why should I do that?
So I kind of expect
that there's three kinds
of people in this room.
So the first kind of people
you'll prefer member functions.
So who prefers member
functions in general?
Okay, that's about 50%.
Member functions, yeah.
Okay, free functions.
Who prefers free functions?
(audience member comments)
I'm sorry.
- [Audience Member] Did
we have these up there?
Okay, so if you use both you can
raise your hand in both, of course.
So about 50% free functions.
Oh this is going to be interesting.
All right and the third kind of people
it's all the people
that they're just here,
because they need some piece
of the five days of conference.
They went to check their email.
It's okay, I fully understand.
You're accepted here, you're welcome, too.
We'll treat you well, no problem.
So believe it or not this is a talk about
software quality in general.
So I would have to ask you now
what do you think is good in software?
What quality metrics should we strive for?
Will take a little too long
and so I've selected a couple.
And I just want to know
from you whether you agree.
And so we've hopefully
found some common ground.
So the first one is, encapsulation.
Who feels like encapsulation
is something valuable,
something that we should strive for?
Okay not everybody interestingly,
but this is probably the majority.
All right, so.
Second one, abstraction/polymorphism.
Is this something good, is this something
that you should have?
Who agrees?
Okay.
Well, perhaps we have
some common ground here.
Cohesion, that things are
following a certain principle.
Okay, this is about 50%.
Flexibility/extensibility.
Oh, almost everybody.
Reuse/generality.
Oh, again almost everybody.
Ah, we have some common ground here.
Testability.
Okay.
90%, my guess and performance.
Okay that's the ultimate argument
for every C++ room, isn't it?
We all want performance.
All right, so I feel we have
some common ground here.
Now I'm bold.
I go ahead and give you hypothesis,
which I'm been trying to prove.
Now the hypothesis is, prefer non-member,
non-friend functions.
Okay and I'll try to
prove this point by point.
Item bite, first of all however,
I have to repeat the solid principles.
So who has no idea about
the solid principles,
who's not heard about them before?
Okay, awesome crowd,
even the cameraman knows
about the solid principles.
For you who have not heard about them,
at least a short reminder.
So my courses are
usually it takes me about
two hours to cover them, but
perhaps a shorter edition doesn't hurt.
So the first one, the S the
Single Responsibility Principle.
This states that everything,
classes, functions, et cetera,
should have a single responsibility,
a single purpose, a single task.
It's a little difficult to explain
because responsibility's
a very vague thing.
But at least you have an
idea of what this is about.
The O, the Open-Closed principle.
This is about being open for extension,
but closed for modification.
Whenever you want to add something,
add functionality, add
classes and functions,
at that time you should not
have to modify existing code.
In the optimal case it's
just adding nothing else.
The L, is the Liskov
Subsitution Principle.
This is about substitutability.
You have a type, derived
type and you can use it
where a point of reference
to base type is required.
It's not just about this technical issue,
it's basically about
contracts to base types.
So driving classes should
adhere to the contract
of the base type.
Then the I, the Interface
Segregation Principle.
Gives you an idea that you
should not have interfaces that
impose a very high number
of dependencies on somebody.
You should segregate them,
you should have minimal
dependencies in interfaces.
And the last one is the
Dependency Inversion Principle,
which basically just says,
if you depend on something you should,
if possible, own it.
If you depend on an
interface you should own it.
Which is also applicable to templates.
So if you define some kind
of concept you should,
if possible, own it.
All right.
After this short repetition, which is
just serving as a reminder,
I have to prove my
statement, my hypothesis.
And the first item I give
you is encapsulation.
There's something that
you've might have heard.
This is also why this
hypothesis might sound familiar.
This is something that's
Scott Myers wrote about
what, about 20 years ago.
And this is the example used
in Effective C++ Third Edition
a web browser.
And this web browser
class has three functions.
The first is called clearCache,
the second clearHistory and removeCookies.
So this is clear functions.
And because it's three I want to have
a convenience function,
something that just clears everything,
a clear everything function
that calls these three other functions.
And Scott is really adamant about this.
This is not what you should do,
this is not how the
class should be defined.
What you should have is a clearEverything
as a free function.
Which is of course a little different.
It is pass reference and on cause
and it's calling the three
public member functions.
And there's good reason to argue this.
So the argument is
clearEverything on the left hand side
is a function that can do everything.
It has full access rights.
It can touch every single data member,
even of course the private ones.
Which you definitely
should have of course.
But it doesn't need this
kind of responsibility,
it doesn't need this.
So pull it out of the class
and there's one function
less that can actually
potentially destroy
invariance of the class.
So this is good, this increases
the encapsulation of the class.
And I know the worst thing
that I can do is just
read some text, but I
feel this is actually
a very, very good choice of words.
So I'm doing it anyway, I'll read it.
Object-oriented principles
dictate that data
and the functions that operate on them
should be bundled
together, and that suggests
that the member function
is the better choice.
Unfortunately this
suggestion is incorrect.
It's based on a
misunderstanding of what being
object-oriented means.
Object-oriented principles
dictate that data
should be as encapsulated as possible.
Counterintuitively, the member function
actually yields less
encapsulation than the non-member.
I have to admit I'm convinced,
I'm fully convinced.
This is a strong argument.
But this is my personal opinion.
And I've met many people
who feel this is not
particularly convincing,
they don't like it.
And so I have to do better.
I have to convince you differently.
And so I come up with the second argument.
Coupling.
So you probably know, you're
aware that dependencies
and coupling are the prime
enemy of our daily life.
So we have to define
or design software such
that it minimizes coupling.
And let's have an example.
I have class X and this class
X has a vector of integers
in the private section of course.
And this class needs to do something.
Now the usual do something function.
But in this function one
of the first steps is
I need to reset the values.
All right, I'll do that, do that.
I had a member function,
a private member function
gives us an implementation
detail after all.
And in this function I just
reset all the integers to zero.
So reset is not clear as you see.
Clear would mean
I reset the sites to zero, et cetera.
Reset, when I say reset I mean,
the site should stay exactly the same,
but all the data, all the
elements of the vector should be
returned to the default state.
Now for an integer this is just zero.
This looks okay.
However, there is something
that we do not see.
So how many dependencies do I have
in the reset values function?
Is it one?
Is it more than one?
Okay, tough question I know.
So there is something that we don't see,
but this is actually a real dependency.
That this pointer,
that this pointer actually
makes this function
depending on
class X, yes I need it
because I internally
access the values member.
But is it really necessary?
Is this truly something
that I have to depend on?
I would argue actually no.
The only thing I have to do is I could add
a free reset values function.
A free function that is
passive vector to none cost.
And in the function I
can do exactly the same.
But how many dependencies do I have now?
A single one.
I'm only depending on
vector and this is now,
this is even the beauty of it.
This is actually better
because I see it in the
interface of the function.
Before I didn't see it.
But it was a private function of course.
So this is actually a real improvement.
And still I see in your eyes
you are not yet convinced.
Ah. (sighs)
You're a difficult crowd, you know?
So I have to do better.
This actually also fulfills the
Single Responsibility Principle.
So this is the first one
of these SOLID principles.
The basic object-oriented
design principles.
Why is that?
Why does this adhere to SRP?
In this form, X has a
private member function
it is doing a job.
So it is apparently access
job to reset the values.
But is this truly kind of a service
that access to implement?
Is this something that
needs to be part of X?
Not necessarily.
This is something that I would argue
could be an external service also.
Less coupling, isn't it?
So.
If you move the function outside,
suddenly,
the real task of the class becomes clear.
The task is not resetting values,
the task is to do something.
And this is more obvious
now, the class becomes
better in terms of the Single
Responsibility Principle.
And still people are not
looking like they're convinced.
Although it's the most important
object-oriented design principle.
I can do better.
It also fulfills the
Open-Closed Principle.
So this actually gives
me an enormous amount
of flexibility and extensibility.
Which I'm going to prove.
Have you ever tried in your career to,
perhaps as a beginner,
to add member function to standard vector?
Very likely you didn't,
'cause it doesn't work,
we all know that.
This is not something that we can do.
But.
You can always, and this is
probably what you figured
pretty early, add a free
function to everything.
Whatever you want.
It's always possible.
I want to add a member
function to standard vector,
not possible, I want
to add a free function,
always possible,
and this is exactly what I've done here.
I have basically extended standard vector.
The only possible way to do
this is to add a free function.
You don't look particularly happy,
but this is actually amazing.
This is so simple, many
people forget about this.
Many people don't even consider it.
But it's actually pretty
easy to extend something,
any type, by adding a free function.
Did I convince anyone at this point?
Okay, a couple of hands.
Whew, this saves my self-esteem, you know.
So.
There's more arguments, though.
For those who are not yet convinced.
Reuse, the DRY principle.
So DRY, meaning don't repeat yourself.
So for just one example,
assume that there's
a second class, yeah I know, I know.
This is a little unrealistic now.
Usually we, of course, only have one class
and put all our stuff in this one class.
But now for just this example
we have a second class.
Class Y with also, coincidentally,
a standard vector of
integers in the private part.
This one is called indices.
And of course, or else the
example wouldn't be worth a lot,
this vector indices
also needs to be reset.
Now I need to set all the indices to zero
for whatever reason.
Isn't it beautiful
that I don't have to code this again?
Perhaps as another private
member function of Y,
but that I can just use whatever service
this reset value offers me.
Okay, agreed I have a vector of indices
and functions called reset values,
not particularly beautiful.
We can improve on that.
We can simply rename the
function, it's a reset function.
Now, now it's resetting
a vector of integers
whether this is values or
indices whatever you need,
it is resetting this.
This is amazing, too.
With just one free function
you've actually offered
a service that anyone can use.
And because it's a free
function anyone can call this.
Wow, actually this is pretty cool.
Again many people forget about this.
(sighs) Still is people
that don't believe me.
I have to do better still, there's more.
I'm now going the
direction of polymorphism.
Static polymorphism of course.
So usually in my courses
when I ask the question,
how does dynamic polymorphism work?
What do people answer?
Anyone?
(audience member comments)
Virtual, the virtual keyword, right?
The virtual keyword gives
me dynamic polymorphism.
And then I ask, okay and how
does static polymorphism work?
What do you?
- [Audience Member] Templates.
Templates, so it doesn't take a second
and somebody says templates.
And then there's silence.
Nobody knows what to say and I say and?
And I have people confused.
And what?
(audience member comments)
Okay, CRTP (chuckles) different topic.
Now I have a second,
I have a second mechanism,
function overloading.
This is a static
polymorphism mechanism too.
Which unfortunately is not
properly used in many contexts.
People don't use it because
they forget about it.
This is truly, tremendously
powerful mechanism.
And in combination with all
the other stuff, amazing.
So what can I do in this function?
Well,
take a look at the red line.
Value is equal to zero.
This is not nice.
As professional C++ programmers
we want to do better.
So what can we do?
We add a reset function.
We add a reset function for integers.
Yeah, I know of course,
you can improve on that
too, but you get the point.
Now inside the function I
can reset all the values.
There's not an explicit
I = 0 anymore, but I just
give this task to another function.
So what does it mean to
reset one of my elements?
This is starting to be really beautiful.
All right.
Now if you take a look
at this code there is
an obviously next step.
It's so obvious that probably nobody
wants to say anything.
What should we do now?
Correct.
Now, I add generic programming.
Of course the solution was
not particularly great,
it was just for integers.
But now I can improve on that.
So I do it here in one case.
I have a template, reset.
This vector is of T, can
be indices, can be doubled,
can be strings whatever,
and I can reset all the values.
What does it mean to reset a value?
I can implement that.
And I can implement this
pretty well, pretty easily
because all I need to do
is add a free function.
You give me a type, a resettable type,
you forget to add reset
function, no problem.
I'll add it for you because
I can it's a free function.
All I need to do is to
add a free function.
Okay, I don't know about you,
I feel this is beautiful.
If I would be a little
better actor I would now
squeeze a little tear
out of my eye and (sighs)
look amazed at this
piece of beauty. (sighs)
Okay, you're a hard nut, you know?
A pretty tough crowd.
I still have to do better?
Okay, here we go.
So I mentioned this, this
is actually an abstraction.
By adding reset functions I have now
created an abstraction, an
additional layer of abstraction.
This is what we are actually,
usually striving for.
We want to make things simple.
We want to start to
understand complex stuff.
And abstractions are just a way to go.
By using all these free
functions we have actually
created an abstraction layer,
this is pretty flexible, pretty amazing.
Okay.
Testability.
All right, this is actually
something that should be
important to everybody,
everybody in this room.
We want to have software that
is really perfectly testable.
All right.
Let's go back to the first example.
We have a privat reset function.
Now I name I reset now, but we're good.
How do I test this function?
(audience member comments)
So this was unfortunate to the silent
that didn't catch anything.
There's multiple voice so type.
C++ testing private function in Google
and you will of course
find at least one thread
in Stack Overflow.
And what are these guys suggesting?
Always.
Adding a friend.
Who likes this approach?
Almost no one and I know why.
This is awful.
This is truly awful.
You have now coupled your tests
to your production code.
This is exactly the wrong way.
Now we have a two way coupling,
test to production code
and of course production code to test.
Unfortunately this is
usually the right answer,
so people click on yeah agreed
and this is what is solving the question.
This is pretty unfortunate.
Okay at least for me,
who does not like this?
Okay, whew.
So, but you've not seen the worst.
Some guy
afterwards says, "Oh you can
actually do better, do this."
(audience laughs)
Of course.
So.
The feedback gives me
the point of, you agree
that this is more awful.
Yeah that there is truly bottom point
and this is probably it.
This is absolutely awful.
And this is a clear indication
that you have a design flaw.
If you have a private
function that you need to test
and you have to bend
backwards to make it work,
then something's wrong.
You have missed something.
Your design doesn't work.
And of course what is
the obvious solution?
Maybe the free function.
Pull it out of the class,
make it a free function.
So in general perhaps it's
not a proper free function,
at least make it a public
function in another class
that you can probably test.
Please never use define private public.
All right, so this is perfectly testable.
It can always call it from anywhere.
There is nothing that I can not,
not test about this
function, so it's perfect.
(sighs heavily) Okay, now who's convinced?
Okay, (sighs) many, many more people,
because testing's important.
This is actually how I get most people.
But still some people
are pretty resistant.
I don't like them.
I don't like free functions.
Okay, and so I have to go
to the ultimate argument.
The ultimate argument that I can give
to every C++ programmer,
performance.
(blows air forcefully)
Okay, I know full well this is
an argument that does
not hold for everything,
so not for every free function,
but it does in many cases.
And this is what I've learnt
at Meeting C++ in 2015,
in a keynote by Chandler Carruth.
And Chandler has to
know, he is optimizing.
So he is in the optimizing
team of the client compiler.
And this is the example he used.
On the left side you see a struct.
It's just a struct, there's
nothing in the private part.
There is some float values, x, y and z
and there's a double value delta,
and a compute function.
Which returns another double.
And then this struct is
used in this fashion.
I create an S and then I,
once of another compute
all these float values,
and this is not a trivial
computation, it takes some time.
I don't know what it is.
Then I compute s.delta value
and then I call compute.
And Chandler complained
that this is something that
I can not perfectly optimize.
And still he sees this frequently in
performance critical codes.
And this is why he
actually gave this example,
to tell people.
Why doesn't this work?
What is in the way of the compiler that
prevents it from doing proper itemization?
- [Audience Member] A dependency?
There is a dependency.
And we've seen it already today.
Okay, I know it's a
tough question at the end
of a conference.
There is something that we do not see,
the s.pointer.
The compute function's a member function,
because it is a member function it had
it's implicit first argument,
the s.pointer.
This is an address.
And there needs to be an address
in order to call compute.
So what the client needs
to do, and most probably
all other compilers as well,
is to create an S in memory,
just so that I can call
the compute function.
I need a valid s.pointer.
If this would not be, if
there wouldn't be a s.pointer,
I don't actually need an S at all.
I do not have to put anything in memory.
This is just
happening in the resisters of the CPU.
So by adding a member function to the S,
to compute function,
you're actually preventing
your compiler from doing
the best he can do.
As I said, this does not
apply to everything of course,
but there is a couple of examples that
where this is actually
really, real important.
Okay. (sighs)
And with this argument,
probably a lot of people are on board.
Oh, if this can actually prevent
perfect performance I
probably should better
consider free functions.
So by now I have almost everybody, right?
Okay.
Yeah, I know it's tough.
I forgive you.
So this is what we should do of course.
So let's take a look again
at a pretty impressive, pretty
extensive list of arguments.
Encapsulation, the Single
Responsibility Principle,
cohesion, flexibility, extensibility,
reuse, so reuseability,
static polymorphism,
generic programming,
abstraction,
testability,
performance.
If you're not impressed at this point I
basically have no idea what to do.
This is virtually every
argument in the book
for what you should do, what
you should have in your code.
And I don't say that member
functions do not have
anything of this, no, no, no.
Don't take me wrong.
I just say free functions are better
with regard to everything on this list.
And this should, it definitely should be
a convincing argument.
(sighs)
And still I get the feeling
there's resistance.
No, I don't accept this message.
That's crap.
And so a couple of you,
a couple of clever guys
come up with counter arguments.
You try to prove me wrong.
Is this even a real idea?
Is this used anywhere?
Okay, good question.
If it's just some vague
idea that nobody uses
it's probably not worth a lot.
However I think I have a
couple of strong arguments.
What about
the standard library?
Oh, okay, so where?
Well for instance let's take.
The standard begin function.
You might have noticed that in C++ 11
they added free begin and end,
and of course all the C and C N functions.
These two functions,
okay these four functions,
complement of course the member functions.
But they can do more,
they can do much more.
So this is the one that
just calls the member begin,
but look at this.
This function the third one, labeled two.
This function is actually
for static arrays.
Static arrays don't have
members, member functions.
Of course.
But the free function
actually gives me access to
also these kinds of data types.
Now what I want to do in my code,
I would like to write std begin,
I don't want to write dot begin.
This makes my code more generic,
it makes it applicable to more stuff.
When you give me a
container of whatever kind
and you missed to add begin
function, which is unfortunate,
but it can happen, of course.
The only thing I need to do is,
I need to write the begin function.
I don't have to adapt my code anymore.
This code is fixed, the code is done.
I add something, I adhere to
the Open-Closed Principle.
That's actually really amazing.
Okay and this is probably worth
a second example, std real.
If you work with complex numbers,
which probably most of you don't, then
you would ask, "Okay how
can I get the real part
"of a complex number?"
And there's a member
function that you can call.
But there's also,
good thing since C++ 11, a free function.
So the free function for complex,
actually the new part is
the one on the bottom.
The real function.
It's just a free function that gets me
the real part of standard
complex, that is not amazing.
What is amazing however
is the stuff that it
here at the bottom, two, three and four.
This is real functions
that have been added
in C++ 11,
in order to enable me to
write proper generic code.
If I have complex numbers
it might also happen
that I have floats and doubles, of course.
I can not call a float, dot
real on a float or a double.
The only thing I can do is
I can call free function.
The standard provides me with these tools.
This is exactly what it should be.
This is what I would
like to see everywhere.
I want my code to be adaptable.
I don't want to change anymore.
This is great.
So yes, this idea is used in many places,
in the standard library, in
Boost, there's other libraries.
These libraries actually
give you the opportunity
to do proper object-oriented
engineering programming.
Okay, some of you are still not convinced.
So.
I come up with another argument.
Come on, this is just a reset function!
This doesn't prove for anything.
Okay, okay, okay, I understand.
I give you this point this is
truly just a reset function
it is pretty simple.
I agree.
I think the confusing thing is
actually the implementation,
so let's get rid of this.
Let's just look at the function itself.
The real question is not,
this is prove something,
the real question is why does it work?
Why is it giving me all these advantages?
It works because reset
fulfills two things.
And this is actually
really important things.
First of all it self adheres
to the Single Responsibility Principle.
It does one thing and one thing only.
Because it does one
thing and one thing only
I can give it a name,
I can give it a clear, crisp name.
Everybody knows what I mean.
Because of that
I can start to overload, I can reuse it.
There is something that I can do beyond
just this being a function.
So the second thing is
it has clear semantics.
And this is the key to
every other argument,
overloading,
making it a template, reuse.
So if you do not,
I'll turn it the other way around.
If you do have functions
that adhere to the SRP
and have clear semantics,
make them free functions.
This is good, you will benefit
from this in a long run.
If you have functions however that do not
adhere to these principles,
okay, then this is probably function set
you should hide from the world.
Put them in the private part of your class
and be done with it.
So this is probably not something that
you'll be able to reuse,
this is not something
that you can build on.
Now hide the ugly stuff, but
enable the beautiful stuff.
So.
This is just a reset function,
I don't really agree.
So no.
This is not a counter argument.
Okay, I take it back.
It is a reset a function, it is not just
just a reset function, yeah?
Okay.
Functions should be encapsulated!
Okay, also a good question.
Isn't this about what
object-oriented programming is about?
Encapsulation?
The prime principle of
object-oriented programming?
It is, right?
The question is,
should functions be encapsulated?
Why is it that we encapsulate functions?
What would be the rationale for that?
I believe it's because we do not want
anybody to call them.
They do something that could,
if used in the wrong way
could break invariance.
This would not work, right?
All right.
But do I really have to put them in the
private part of a class?
Is this the only thing I can do?
What about name spaces?
Why don't you create a detail name space,
this is by the way what Bruce does,
and put all the implementation details,
all the things that could
accidentally do bad things,
if you don't know what you're doing,
in the detail name space?
It's a free function.
It can overload, you can
reuse it, it is testable.
You have all the advantages,
nobody will call detail your function.
I doubt that.
I very much doubt that.
So I don't think that
function encapsulation
is really a thing.
It is a leg of
doing something that is better.
Of knowing about something that is better.
Okay, I have another argument though.
Let's go back to this initial example.
Let's say we have now
encapsulated the function
in a private part.
Okay, it's encapsulated.
But actually I can even
encapsulate it stronger.
I can encapsulate it stronger by
first moving this
function out of the class,
make it a free function and then,
move it into CPP file.
There's no mentioning of this function
anymore in the header file.
It doesn't have to.
And implementation of do something,
which is of course also in the CPP file,
can then use this function,
the reset function,
which is also implemented in the CPP file.
This is even better from an encapsulation
point of view, isn't it?
This is
perfect encapsulation.
Yes, I truly agree.
I'm convinced.
Free functions are
better for encapsulation.
Okay. (sighs)
Some of you are inventive.
Some of you have a couple
more things on your mind,
so you know more.
And you come with another argument.
Oh, okay no this is not an argument.
IDEs don't help with free functions,
but with member functions!
Okay, this must do the trick, of course.
IDEs don't help you with free functions.
But if you have a string
S, you can say S dot
and all the what not 70
whatever member functions
are listed and you can just select one.
Okay this is a strong counter argument.
Okay.
So if you're a Visual
Studio user it probably is,
or any other IDE.
Who uses certain IDE that helps?
It's a couple of people.
And you would very likely argue,
my IDE only helps with member functions.
This is actually a good point.
This is something I can not argue against.
This a reality.
This is unfortunate.
The basic thing what you have to do is,
go back to your window,
Microsoft, Jetbrains whatever and say,
"Okay look here.
"This guy told me I should
have free functions.
"I want support in my IDE for that."
Okay.
The other alternative that you might have
in the future somewhere
is
uniform call syntax.
You might have heard about this.
So there was a suggestion in 2014 that
it should be irrelevant
how you call a function.
You should be able to say
S dot, call a function,
which also calls free functions.
And this is now a quote from Bjarne, who,
yeah I just read it.
Note begin(c) and c.begin()
for range-for loops
and in general code.
Why do we/someone have to write both?
If c.begin() exists,
begin(c) should find it,
just as x+y finds the
right implementation.
In early 2014, Herb Sutter
and I independently decided
to propose a unified syntax.
To my surprise many people
came out strongly against
x.f(y) finding f(x,y)-
even if member functions
were preferred over
freestanding functions
by the look up rules.
I received email accusing me
of "selling out the OO crowd."
Oh my. (sighs)
So.
I really hope that after
the arguments I gave you,
after I showed you the
advantages that you get
by free functions,
you do not believe any longer
that anybody is selling out the OO crowd.
OO program is not about
putting everything to classes
and hiding it up somewhere.
There is definitely much, much more to it.
And I believe that Bjarne was actually
enabling the OO crowd.
He was enabling them to do better things,
to do more things.
So this is pretty unfortunate.
I know, there's a lot of detail
that still need to be discussed.
There's also a lot reservations that
over how the uniform
look really should be.
But if you would have it,
then this problem of IDEs
would be gone entirely.
You could actually very easily
work with free functions and
you IDE would assist you.
So.
Another argument that you might have.
Programmers cannot find free functions
as easily as member functions.
Okay, also an absolutely valid argument.
If you want to find
functions but you look,
you take a look at the class,
inside the class body, in the public part,
it is all the functions that it can use.
Where's the free functions?
Could be anywhere.
So this is actually also a good argument,
but not an argument that is
entirely impossible to solve.
It's basically just a matter of how
you structure your code.
So now, go ahead, tell people,
this is a good thing, this is
something that you should do.
You should use free functions.
And where do you put them?
Well one idea, just off
the head of my mind is
to declare all free
functions that are available
directly after the class of the body.
So if somebody wants to see,
"What can I do with a class?"
He takes a look at the member functions
and the free functions.
He knows where to look for them.
Of course also documentation
would help but,
I know we don't document.
(light laughter)
So.
I believe this is just
a matter of discipline
and agreement, for
instance within a company.
Have a guideline.
Where do I put my free functions?
This is all it takes.
So this is not an argument that
can completely erase all
of the good arguments,
the good things that are here.
And still you're not satisfied, oh my.
In combination with ADL
free functions mean trouble!
So who knows about ADL?
I turn it around, who
doesn't know what ADL means?
Okay, okay, argument dependent look up.
So you probably never,
if you have a string
and you use the output operator.
It finds the operator.
You don't say std output operator.
It just finds the right function.
It takes a look at that
name space then it says,
"Ah, there it is, this is what I need."
So this is ADL.
And of course ADL has certain
potential for trouble,
you're absolutely right.
I give you an example.
Complex numbers again, I know.
I'm a numeric guy.
Complex number is A and B
and I say a.min(b).
Does this compile?
Okay, you don't use complex
numbers, I realize that.
No, it doesn't compile.
Minimum's just not a relation that it can
apply to complex numbers,
what kind of minimum?
That's not verifying so,
get a completion error
there is no member named
min in std complex.
Okay, now I change the
example a little bit,
I say min A and B.
Does this compile?
(audience member comments)
I'm sorry.
(audience member comments)
So, is there an implementation
is the question?
Actually there is.
(audience member comments)
Std min, correct.
The standard library has min function.
What are the arguments
from which name space?
The standard name space.
So ADL goes ahead, takes a
look at the standard name space
and okay, there is is min.
We are lucky though we get
a difficult error message.
And this says,
so if you see the red
part, the third line,
return x smaller y.
It complains the complex
numbers do not have
less than relation.
Whew, at last I can save.
But it's not a particularly great save.
It's not particularly
great because it actually
chooses the function,
it just doesn't happen to compile.
And this is the trouble
that ADL can bring,
this is true.
However.
Again the problem is
not the free function.
The problem is how I
define the free function.
Unfortunately up to this
date min just takes anything.
If you take a look at the signature of min
it takes a T.
T can be anything.
So this can be used for
really every single type.
If however, hopefully
eventually get concepts,
this could be implemented by I need a
type that adheres to some
less than comparable relation.
And then this function
wouldn't be selected anymore.
So this is a weakness that we
currently have in the language.
But there is hope that this improves.
Okay, free functions get just better, hey.
All right, so.
This is not entirely true.
Yes, there is some problems with it,
but this is not your major concern
or not your major problem I believe.
Are you satisfied?
(chuckles) Who said no?
Not yet?
Okay, I know you still
have counter arguments.
I'm using virtual functions,
so I cannot use this stuff anyway.
Are you sure?
So, let's assume that you
have a virtual print function.
And you would argue, well,
"No, no, no, no, I don't
need a free function."
I would say well probably you do,
because you probably want to
support the output operator.
And the output operator
is a free function.
So yes, this idea is still for you.
But,
there's a suggestion, it's not something
that you have to do, but
this is also enabling
other people to use your code.
So use free functions in order to
wrap virtual function calls,
and get a homogeneous interface.
So you have other free functions
and then you have virtual functions,
such as number functions.
The interface is not homogeneous.
Some are member functions,
some are free functions.
Enable people, make it easy for them.
Wrap the virtual function
in a free function call
then you have a homogeneous interface.
This also might remind you about this idea
of a non-virtual interface idiom.
This is going in this
direction, this is not bad.
You might actually also gain from this.
So even if you're using virtual functions,
free functions are not
completely off the table.
In other words you're not off the hook.
And somebody is desperate.
Oh my god, does this
mean we should convert
to functional programming?
Okay, wait a second.
Do you really know what
functional programming is?
So is this functional programming?
Is the reset function
a proper implementation
of functional programming concept?
No, it's not.
So it changes the vector.
This is against the rules
in functional programming.
So do not be afraid.
This is not functional programming.
But you've actually made a
very, very, very good point.
This could be a function that is used
in function programming if
you rewrite it a little bit.
So actually you have
accidentally of course,
uncovered the hidden, the bonus argument,
pro-free function.
This is multiparadigm.
A free function is used in any paradigm.
It's used in object-oriented programming,
it's used in functional programming,
it's used in procedural
programming, whatever you have.
If you use free functions,
your code actually,
you're enabling yourself
to use all paradigms in combination.
Whenever anything is...
Something is beneficial
for you, this is great.
It's just a small step
between our programming
and functional programming.
Just a little step.
Wow.
Okay, you're hard to impress.
Really hard to impress, but
I feel this is truly amazing.
Okay, and now the obvious question.
It had to come.
Should we avoid member functions entirely?
Okay.
Do not go to Slack or any
other social media platform
and now quote me with, "The guy told me
"I should not use member
functions anymore."
No.
This is what I did not say.
I did not say, please make
all your functions free functions.
What I basically tried to give you
is the suggestion that free
functions are very valuable
for many, many of your functions.
There are several functions
can not be good free functions.
In case you're wondering
how we should decide,
free function or member function?
Take a look at standard vector.
Standard vector has a
lot of member functions,
it's actually quite a
number, more than 40.
But all of these functions
could be member functions,
all of these functions have some access
to data members in the private party
or they modify them.
They have to upkeep invariance.
So all of them need to
be member functions.
But this is it.
Apart from that there is
nothing in standard vector
that does not have to be member function.
No function is there
that shouldn't be in there.
This is a good thing.
And I believe this might help.
This is something that
Scott Myers proposed in
what, 2000, February, 2000.
Any algorithm that helps you decide,
should it be a free function
or should it be member function?
So if F needs to be virtual.
Okay, obviously for technical
reason it needs to be
a member function.
Else if the F is the
output or input operator or
if F needs type conversion
on its left-most argument.
Then it should be a free function.
It should be because else
it doesn't work properly.
If F needs access to
non-public members of C.
Well, okay this is true if you need
special access, still
you may get a friend.
You still have the free function,
it does not have to be a free function,
but it can access private data number.
Else if F can be implemented
via C's public interface.
Okay, no question here.
This is where the algorithm usually stops.
If you do not have any reason to access
private item numbers or
private functionality
then there is not reason to
make it a member function.
So make it a non-member function.
And else?
If none of the other things
apply, make it a member.
And all the getters and
setters are down here,
in the else part.
And all the functions
that truly need access.
All these functions of course
have to be member functions.
It is perfectly okay to
use member functions.
I'm not against member functions.
All right.
But I have learned the opposite!
All right, so who has
learned exactly the opposite?
Who has learned that you should use
member functions primarily?
Okay this is interesting,
only a few hands.
I would have expected that
this is almost everybody.
However, this does not mean that,
member functions truly the better choice.
This just means that we
have to definitely update
how we teach C++.
This is a general problem.
This has been discussed
many times over and over.
How do I properly teach C++?
Usually instructors have
to a knowledge so this
it was the time when I started C++.
Everybody knew about Java and everybody
was teaching Java, just with C++ syntax.
This is unfortunate.
This is something however
that you can help to change.
You can spread this information.
"Hey, by the way I've heard
"free functions are actually
pretty cool, pretty powerful."
Try to use them and spread the word.
And so perhaps at some point,
people in universities or
wherever you learn programming
actually hear the message.
Free functions are the way to go.
Okay.
And there's a few people left.
A few people who believe
(sighs) it's member functions.
I'm still convinced it's member functions.
And they come up with
the ultimate argument.
Free functions are just backward
and C-style programming!
Oh I've heard this argument so many times.
It's not really an
argument it's, you know.
Is it really?
Are you convinced that this is backward
and C-style programming?
Okay.
Let me just show you one thing.
And perhaps this actually
may make you think
about this entirely differently.
You know this function,
I'm pretty sure about it,
the copy function, std copy.
The copy function is pretty simple.
I would even argue it's trivial.
It contains a full loop of instead nothing
but copy a couple of values.
But.
This function actually adheres to,
first of all the Single
Responsibility Principle.
It does one thing, it copies.
There's nothing more, it
does not allocate any memory.
This is what it inherits
kind of from mem copy.
It also adheres to the
Open-Closed Principle.
This function never has to be adapted.
Never.
If you want something to be copied
well you just have to adapt
your type accordingly.
This function is sealed.
This is good, I have to add stuff
in order to make this work.
It also adheres to the
Interface Segregation Principle.
The name does not really give it away,
but this is templates,
there is no interface,
but there's concepts.
And the concepts in the copy function
minimizes semantic
requirements on your type.
It's truly the minimum amount of
operations that you need to provide
in order to be able to use copy.
This is adhering to the
Interface Segregation Principle.
And last, but not least
this also adheres to
the Dependency Inversion Principle.
It does because it owns its own concepts.
It defines what operation
you need to provide
in order to work.
I know this is a little hard to get,
it's probably something that
you have to ask me afterwards, but
this actually works too.
So this simple, this trivial function
adheres to four of the
five SOLID principles.
The basic object-oriented
design principles.
There's only one missing.
The Liskov Substitution Principle.
It cannot adhere to this
principle 'cause it's a function.
Liskov is about types.
But you and your type,
you can adhere to the
Liskov Substitution Principle
if you provide an InputIt operator,
or an OutputIt operator.
Now you have to adhere to some behavior,
expected behavior in
order to make this work.
Now we should be impressed.
Now we should be impressed because
now perhaps you understand
why the stl works
so splendidly, why it's so awesome.
And why Scott Myers among
others say something like
there was never any question that the
standard template library
represented a breakthrough
in efficient and extensible design.
It is a different kind of
object-oriented programming
if you just see this as adhering
to object-oriented principles.
This is why it works so well.
This is why it's so amazing.
And so.
This is not an argument,
I don't believe it.
So.
By now everybody's convinced.
There's no more arguments.
Nobody here has anything more to say.
And you surrender.
"Okay, okay, I will use
free functions from now on."
And the hypothesis it becomes
your new guideline.
Prefer free functions.
Prefer non-member, non-friend functions.
Okay, perhaps it's not your guideline now.
But at least you have gotten a little idea
that there is actually some value.
So if you until now,
had only member functions,
give free functions a chance.
They might actually improve
the quality of your code.
And so.
To summarize.
Free your functions!
Thank you very much.
(audience applauds)
Right, there's a couple, time
for a couple of questions?
I know I've taken all the questions
already kind of.
(light laughter)
- [Audience Member] You
didn't mention about parking.
You could have free
functions as static functions
in a class, I mean sometimes to
not polluting the name space.
You mention anything about that?
So the question is should I park
free functions in classes?
Note then they are not
free functions anymore,
then they're member functions
also, static member functions.
This is sometimes the right thing to do.
But I found it rarely
the right thing to do.
A better way is to have a name space.
A name space saying,
oh so detail name space
put the free function there.
If you want it to be found
then it's probably not
in a detailed name spae.
If you don't want it to be found,
it's in a detailed name space.
I found name spaces much more versatile
in this induced context than
static member functions.
But it depends.
I don't say I'm against member functions.
- [Audience Member] Okay, thank you.
- [Audience Member] Many of your arguments
were very convincing.
There's one thing that
wasn't on your list,
which is mockability.
So if you want to test,
test a function that uses
one of the free functions
and you want to avoid those side effects
in your test 'cause it's slow or whatever.
If I put that free
function inside a class,
even if that class has
no members or anything,
I can subclass it to mock that function.
How would I do that with a free function?
So how to mock a free function?
So technically not really
possible, that is true.
This would be a counter argument.
However, I recommend the
talk from Monday or Tuesday.
There was a talk about mocking class,
mocking in C++.
And this is not really a thing in C++.
So there is other ways to do it in C++.
This is more of the Java
world I have to admit.
In C++ there's other
ways to design things.
So mocking, sometimes yes.
As I say I'm not against member functions,
I'm not against virtual functions.
This is not what I want to convince about.
You should have the problem very rarely.
- [Audience Member] Thank you.
- [Audience Member] So.
Are you convinced?
Are you convinced?
Yes.
Yes!
(audience laughs)
So I came to the
- [Audience Member] motivation
for using free functions
a little bit different route.
We have a large code base and we had lots
of member functions that were
accessing the member data
and modifying it.
Therefore it made it
hard to put break points
where all the member data
was getting modified.
So that's another benefit is
you're limiting access
to your member data,
or modification access.
So.
He says he had a large code base.
And in this code base
it was actually benefit
to use free functions because
they could not access
all the data members.
And I'm very grateful for this argument.
This is something that you
cannot convince people about
that's hard to show.
But this is actually true,
this is what I also found.
So usually, even in a large code base,
do not be afraid to use free functions.
You might in the end
actually benefit from them.
So they will not break everything.
- [Audience Member] And
so another motivation
along with that was
lots of member functions.
Well, those needed to be
grouped into logical groupings.
And so those go into a name space
that's logical name space, so subdivision.
Which is reasonable.
So all the free functions
went to namespace.
Yup.
Okay.
- [Audience Member] So
given that we don't have
unified calls syntax,
an argument against using
free functions is that
if you have a chain of
function implications
it looks kind of ugly
with all the parenthesis
and it's hard to tell what's going on
with the free functions as opposed to
something dot, something dot something.
Do you have anything to say about that?
(sighs) Okay this is of
course pretty subjective.
So the question was that,
chaining free functions looks ugly
in comparison chaining
member function calls.
Some people agree.
I don't.
I like the free functions.
I think I (laughs) got this point.
There's a lot of demiter,
which you might know,
which means you should not
have a long chain of calls.
If this happens there's another problem.
And so you should not have
this very often hopefully.
You should have perhaps
two nested function calls.
So free function one, free function two.
There should not be a huge chain of calls.
So.
I would argue there shouldn't
be long chains at all.
- [Audience Member] Thanks.
Yeah, please.
- [Audience Member] So another.
You had mentioned
wrapping virtual functions
with free functions.
But also the opposite works too.
'Cause you have your virtual functions
call a free function.
Absolutely.
And so you then don't
- [Audience Member] have much
code in your virtual function.
Absolutely.
This is actually
something I really forgot.
You can of course also call free functions
from virtual functions.
This is why type erasure
works in the first place.
This is the technique
used in type erasure.
Or it is one
piece in the puzzle.
This is actually possible
too and I recommend it
because the free function
can be very easily tested
and the virtual functions certainly
does not have to be tested anymore because
it's a simple function call.
All right, this concludes the session.
Thank you very much.
(audience applauds)
