Cookies   I display ads to cover the expenses. See the privacy policy for more information. You can keep or reject the ads.

Video thumbnail
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?
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)