♪ I'm with the big shot, yeah ♪
♪ Yeah, yeah ♪
(lively music)
(attendees applauding)
- Bjarne Stroustrup has
been a guiding force
for this community and has done a lot
of work to promote C++ and to
build a community out of that
and so I wanna,
I'm looking forward to his talk today
where he's gonna tell us a
little bit about concepts
and what that means.
Thank you very much, Bjarne.
(attendees applauding)
- Thank you very much.
Okay, wow.
If I could see all of
you, I'll be intimidated
but fortunately, thanks to the lights,
I can see about two lines here.
So I'm going to tell you a
little bit about concepts
and in some ways, that's a very old topic
and others thinks it's new.
You can start using it.
Some of us has been using it for years.
It's getting into production in places.
So I'm going to talk a little
bit about generic programming
then I'm going to say what do we actually
get out of concepts
and what are concepts and
how do you build useful ones.
So, basically, you can
go back to the early aims
of David Musser and Alex Stepanov.
Basically, wanting to get
generic programming in
as a discipline, providing
more flexible, better code
with a focus on,
on algorithms
as opposed to data types.
And my aim is sort of
fairly simply stated,
making generic code as
simple as non-generic code.
This is one of these
modest and ambitious aims
that can drive development,
so I'm going to do that.
Obviously, when you have generic code,
not all code can be as simple
as if you're just writing code
based on knowing that
everything is a double C.
So, more advanced generic code should be
not unnecessarily complicated
but it can't quite be as
simple as non-generic code.
And one thing I would like to emphasize
and I feel I probably
won't be able to do it
as much as I would like
to is it's not just
for foundation libraries.
There's a lot of application
code that is generic
and you shouldn't just think about it
as something, oh, a
standard library that you
don't really need to think about.
I was talking to a bunch
of theoretical physicists
a couple of weeks ago.
It's there in their stuff
that's certainly not foundation library.
I talk to generic,
embedded systems
programmers that use it for
certain aspects of device controls.
This stuff, concepts
is not just a hysteric,
it's not academic at all
and it is not just for
foundation libraries.
Basically, the whole game
is to write a better code
and better code has all the
usual requirements here.
Nothing has much to do
with generic programming
but the point is that if
you write generic program
in good generic code, it helps
with all of those things.
And it's basically a significant help.
I really don't like talking much about
individual language features.
A language feature and
isolation is usually boring
and you can talk about
where you put your commas
and semicolons and some
people like that, I don't.
I tend to fall asleep over that.
It's necessary at some level
but I'm trying to get the big picture.
So this is not a talk about
language technical details.
if you want to know the latest about
the debates in the Standards Committee,
well, go to one of the other talks
to the other committee panel
or something like that.
This is not what I'm trying to talk about
and there are at least four
talks at this conference
that goes into details about
generic programming and concepts
so that's another good reason for me
not to go into those details.
So the concepts
have been moving through
the standards process, too
slowly, in my opinion of course
but I'm known to be impatient.
And basically, the TS was
approved two years ago
and we have the explicit requires clauses
in the working paper now.
Barring utter disaster,
it will be in C++20.
We have the shorthand
notation so that you can
instead of just saying I want the type N,
I can say I want here a
random-access iterator
and we have requires expressions
to express things directly
in the language.
Not quite yet.
We hope to get ranges into the
working paper in San Diego.
That's less than two
months away and again,
that seems to ready to work
which means that you are algorithms
and containers and stuff like that.
I can start using concepts
and you get the benefits
right out of the box with C++20.
There's work going on the
function declaration like syntax.
That's not settled yet
and my guess is you won't get the concept
type name introduces.
If you don't know what
that is, well tough,
you can find out and anyway,
you probably won't be able
to use it in production
for another couple of years.
It's all available in the
GCC implementation now.
So my main sort of thesis here
is that genetic program
is just programming
and my aim is to make this
more true than it is now.
And this implies that we need to improve
some of our type checking
to make type checking
for generic code much more like
checking for ordinary code.
Eventually, the generic
code will be ordinary code.
We need to improve the syntax.
The template syntax is clunky.
It has been clunky since
the simplest syntax didn't fly in the 80s.
And we have to organize
our code for generic
and non-generic code in the same way.
Not all of this header stuff
or templates and .cbp for code,
that's the modules stuff
so I'm not going to talk about that.
So basically there's a history to this.
In the '80s, most of the '90s,
if you wanted generic code in C++,
you ended up using macros
and I have a paper from '81 that says
we need generic programming and macros
will just do it for us.
I was right about the first part
and I was very wrong
about the second part.
Macros just poisons
everything including your IDE
and your editor and scrambled your brain
so we have to do better than that.
In '87
and on so now,
I have some fairly specific
aims for templates.
I want them to be flexible.
At the time, I said I
don't want a language
that can only do what I can imagine.
I mean maybe my imagination
is good but sure,
it isn't as good as the union
of people in a room like this
and I want a zero-overhead,
otherwise, people are right
this sort of ugly pointers
and array kind of code
that they then think is more efficient,
it often isn't and it's
certainly less maintainable,
than a good generic code.
So it had to have zero-overhead,
this is C++ has to be
used for really critical
embedded system stuff,
it has to be used for
high-performance stuff
and it is today.
And then of course, I
wanted good interfaces
because good interfaces is
essential for large software.
That's how we partition our code.
We only have two ways of simplifying code.
It's abstraction and divide and conquer.
Divide and conquer means putting a barrier
between two parts of the
code and we get there.
And as I used to say, two
out of three ain't bad
but it isn't good either.
So I've been trying to
figure out how to get away
from the basic templates,
unconstraint templates,
header-only organization
because it's fundamentally not right.
It may be good enough.
We've done superb things as
a community in this period
but we can do better.
Okay, so let's take the time
machine, go back to '78.
And that's how we wrote code.
This is K&R code.
Say that square root returns a double.
We give it a two and it crashes, of course
because two happens not to be a double.
Well, so what?
Well, we didn't say it was a double.
All we can say is we can
go over to the source code
and look at it and say
that square root takes an X
and it treats it as a double.
It does not say you must give it a double
or you get an error, get a runtime error.
No, you get a crash.
And I had sort of one look
and another look at this,
couldn't quite believe what I was seeing
and made some changes in the last,
in the first two months
of the C++ project.
See the classes that was caught,
so now we can write double
square root of double.
I mean I want my square root
and I want the declaration to
say that it expects a double
so that if we get square root or two,
I get the right answer.
It converts to two to 2.0.
Similarly, if I give it
some rubbish like a string,
I get an error right there.
This made a huge difference and
I also cleaned up the syntax
so that the definition now
looks like declaration.
And if you've been writing C++,
you probably never seen anything else.
If you've been writing C in
the last sort of 30 years,
you've been seeing
things roughly like that
and it's an operation like that I think
we're trying to do with
concepts relative to checking.
Go back to '88 and we're saying
that there must be a type
which we will use as an iterator.
And since we don't know too
much about the interface,
it just says there must be a
type and we'll use two of them.
It has to be in the header file
so that we can do some compilation
and I can say a vector
of integers, that's fine,
list of integer's fine, vector of S, fine.
Everything works fine
so we start using it.
And then sorting vectors happens to work.
Sorting lists doesn't work.
From deep down in the
implementation of sort,
we will be told that there is
an absence of a subscript
or a plus or something
to some type that we
probably haven't heard of
because it's an implementation detail.
And if you want to sort the vector of S,
well, because we couldn't
get the definitions
of equality in less than four types,
it'll just tell you that you
can't compare two structs.
And the error messages
are spectacularly bad.
This is because we have duck typing.
If it looks like a duck,
if it waddles and it quacks, it's a duck.
Unfortunately, of course,
it could be a rubber ducky
or something like that and
we get these strange errors.
We really want to be able to say
that we want something
better than just a type.
And on the other hand,
templates was a massive success.
It's one of the things that kept C++ going
and at the edge of things, we
have flexibility, type safety,
specialization for irregular types
and the basis of template, lot much,
template metaprogramming
and a lot of type based optimizations.
The result is great
flexibility, great performance.
And has huge flaws, as I
pointed out, the syntax,
I mean, even its mother
couldn't appreciate it.
Duck typing, error messages.
The overloading with templates
is really quite complicated.
Code organization gets messy
and the compilers get slow,
very slow.
We have to do something about it
and I would like to address
all of these aspects,
not just fiddle with the corners of it.
So let's see what I want
and what we are getting.
I would like to say that
sort takes a sortable.
Concept is a specification
of what is sortable.
And you can look it up in the standard.
It says that you have to
have a beginning and end.
Being a sequence.
It has to have random
access and a value type.
The element type has to be
something you can compare.
Fine, unfortunately, in
the previous 20 years,
compilers and users don't read the manual,
however, now, we can write
this so that it works.
I am using the concept
TS notation just now
because then, I can test my code.
This is not likely to become C++20
but it's in the TS.
So, if I sort a vector, it
matches the requirements
of a vector of integers.
It matches the requirements of sortable.
List doesn't because it
doesn't have a subscript,
doesn't have random
access and sort of the Ss
doesn't because value type doesn't have
the comparison operator.
So I get an error right there
and it should be readable.
They will become even more
readable over the years
when things get tuned
to use concepts well.
And now, I can now sort
anything that's sortable
and the implementation of sortable
could simply be to call the old sortable.
And if you want to be
more flexible, I can say
I actually would like to
sort lists even though
they are not sortable.
They don't have random access
and so we just define
the notion of something
that lists like which has the
properties of being a sequence
with elements that you can compare
and I can implement them by putting them
into a vector sorting them
and putting them back again.
That's simple and quite often,
a reasonably performing
regulation and if we do this,
once it sees the list, it
will say oh, I can't do
the simplest sortable but
because of lack of property
but I have enough
properties to call the list
so we'll do it.
So we're getting the
overloading like we got
the overloading for ordinary
types in the standard language,
well, almost 40 years ago.
And the rules for overloading,
you can look at them up,
a much simpler than four basic types.
And by the way, as ranges are
now coming, we can simplify.
See, why do I have to say
begin and end all the time?
I mean one of the principles
that we're trying to work towards
is to make simple things simple
and the simplest case
of sorting a sequence
is to sort all the
elements of a container,
anything, we just give it the whole range
and it'll figure out what the details are.
So things are improving.
Notice also, I didn't
say what the element type
of the vector is.
I can now deduce it.
We are moving ahead and
it's getting simpler
to write good code.
So, if we look at it,
when I want to make ordinary
code and generic code the same,
I have to think about types
and I have to think
about how concepts fits
into a type system.
And a type basically
specifies what operations
I can use on an object
implicitly or explicitly
and it relies on function
declarations and language rules
to make sure this works
and in addition, a type
specifies how an object
is laid out in memory.
So it says, what can you do to an object
and how can you make the object.
A concept on the other
hand, basically specifies
how you can use an object.
How implicitly or explicitly
what operations you can use
and that is specified
in terms of something called use patterns,
basically expressions and it reflects
the rules of the language.
And it says absolutely
nothing about the layout
of the object.
And ideally, that would
be the only difference
between types and concepts.
We are close to that.
So you can think about
simple objects of concepts,
concepts that only take one argument
as basically something that
is how to use an object
as opposed to how to make it.
Okay, so here's an example.
Types and concepts.
I have here a concept which is same
which is that a capitalized
Int is really an int.
And I can now start writing
code with that concept.
So x1, it will take
anything that is an int
and seven is an int so it'll do it,
or I could use the integer directly.
I can do operations and again, I can say,
for the capitalized Int,
I'm saying the result
must be something that isn't int
and the other one it says it is an int.
And I can pass arguments
and I can overload on it.
This looks very, very
similar and it is deliberate.
I even thought of using a
font that'll make it easier to
tell the difference
between the capitalized Int
and the lowercase int and decided no,
actually, the point is they're similar.
The syntax here made immovable objections
in the Standards
Committee so it's unlikely
you will be able to say sort a sortable.
You will probably being
able to say sort a sortable
or throw stuff.
People seem to like that better.
It's a compromise proposed
by Ville Voutilainen.
So you can get very close
to what I'm saying here.
One of the main reasons
this is a discussion
is sortable of ref ref.
if that was a concept
then would be a template
and the ref ref would be
a forwarding reference.
If not, it will be an r-value reference.
I have taught concepts about five years.
So, many dozens of people talk
to many hundreds of people,
I've never seen this kind
of stuff in real life
but that's a fact to
the Standards Committee
and I think I can live with
what we are likely to get.
So, that's sort of as
much as I'm going say
about what the technicalities
about concepts.
I'm going to talk about what benefits
we might get out of it.
By the way, my section breaks has people
who contributed to this.
Andrew Sutton there is
the initial implementer
of the stuff you can get to see now.
And he's worked very hard on specification
and implementation.
So basically, concept supports good design
and they are doing to
design using templates
roughly what classes did
to ordinary programming.
It allows us to structure our code better,
it allows us to think about code better.
If I'm going call something
with a couple of points
like draw a line, you
can do it the old way,
int, comma, int, comma,
int, comma, int, comma, int
and start wondering what the ints mean
or you could have a point
comma point or point comma box
and be more specific
about what your sign is.
That kind of way of
changing the way you think
is what we're after.
It gives better reliability
and better maintainability.
I have some practical
experience with that.
And again, for overloading,
that's important
because overloading is the
basis of generic programming.
If things aren't called the same
when they're doing the same thing
semantically to different types,
you can't write generic code.
We have to go there.
So, let's see some example here.
Here's the classical example.
The slightly simplified version of advance
from the Standard Library.
We can say that if we are
given a forward iterator,
we have to do things the slow way.
Dum-ti-dum-ti-dum-ti-dum-ti-
dum, go forward.
If I get a random access iterator,
I can just go or there in one hop.
This is a very important difference
and we have it in the Standard Library.
Now, we can write it as simply as this.
Basically, give it a vector and advance
is a very simple operation, or one.
If we give it a list,
it will be relatively slow operation, OM,
and basically getting this kind of stuff
simplifies our code relative
to what we have to write today
and makes it much more similar
to other kinds of code.
And note that we are not actually saying
you have to write a concept hierarchy
and make your code rigid
and only do the things
that has been pre-declared to work.
Concepts are predicates,
we just figure out which
predicate matches the best
and get on with it.
The code is simpler.
So yeah, as I pointed out,
overloading is fundamental
to generic programming
the way we do it in C++.
We have been using a lot of traits
which can be quite complicated.
If you looked at code using enable_if,
you know it can be headache-inducing
and also, the workarounds using
these programming techniques
on basically on type code,
the fully generic stuff,
it gets quite complicated
and slow to compile.
Type checking happens at the end
at the very last moment and that's,
at least it's type safe
for some definition,
it's type safety but it
doesn't give the errors upfront
and it gives the compiler a hard time.
So let's see an example.
Conditional properties.
They're very widespread these days.
Basically, we want to say
that something like a class
offers a property if
and only if another type
behaves in a certain way.
So here is the classic pointer type.
Smart pointer.
It could be a unique point,
it could be a shared point or something.
All of them does the equivalent
to what you see there.
You have an operator dereference
if and only if
the thing with the
element type is a class.
Otherwise, you have to dereference.
So that's expressed very directly.
You get a dereference.
It requires that T is a class.
Take a more complicated
example, I have a class pair.
Still borrowing examples
from the Standard Library
and still capitalizing my type
so that you know it isn't
really in Standard Library.
So I want to be able to make
a pair out of two values
and I want to make the
pair out of two values
if and only if each of the two values
can be converted into the
appropriate type of the pair.
And so I write this.
There are two element types.
if their types are convertible
to the appropriate, da-da-da-da-da.
In other words, the code
reflects very directly
the way I expressed what I wanted.
This is nice.
To compare, for those of you
who haven't seen too much
enable_if code, here's
the simplest version
of the pointer version
written the old way.
It says that there's a,
there's an arrow operator,
dereference operator if,
well, you can see the stuff there.
This is sort of painful and
particular, it's not universal,
you can't use the same
technique everywhere.
I was thinking of showing
the pair constructor
and I decided not to because I had trouble
fitting it on a slide.
For starters, there's no
place in a constructor
to use an enable_if.
Secondly, you have to deal
with the variety situations.
It gets very messy.
Concept maps the way we
think about our problems,
the workarounds do not.
So we simply cut out a part
of the thought process.
That's good.
Some people who has tried to use auto
and fully generic stuff has find
that it leads to some problems.
Every new feature get
misused and overused.
That's fine, I love auto and
so does a lot of other people
so it gets you all used.
and one thing we found was
that people call a function
and they put the result into auto.
That's reasonable if there's generic code.
You don't know the exact type
or you don't want to bind it
to a particular type just yet
so you just bring the generic code forward
and you get things like foobar x or y,
you put it to some value or some type.
Fine, except we find that
the programmers keep looking
into the header files and
flipping forwards and backwards
in the code and readability
is seriously decreased.
This becomes a bug source.
So, the response for now
has been put a comment in.
Every time you assign something
that's not obvious, really,
it's alpha type, put a comment on it.
Sure, make_shared, you
don't have to have a comment
because you know make_shared
makes a shared pointer
but a lot of cases, if
it goes beyond that,
you have to put comments in
and your code gets uglier,
larger and comments are not always right.
The concept, you can say
well I'll take anything
as long as it's an input channel.
And you can do that in any context.
That clears our code a
lot eliminates programmers
flicking back and forth between
different parts of the code
which is very distracting
and breaking concentration
and you just get much better code.
Readability is one of
the benefits of concepts.
And then, I don't know.
Familiarity is a strong force
but you look at that template there,
it's really quite clunky.
And for new things, there are scary people
want prefix keywords and
that's how we got to here.
Names become very important because
there's not really a
type system here in play
that says what the type name really means.
I mean type name input iterator means
I hope that the input
iterator is an input iterator.
It's just a matter of hope.
And this was what we got out
of some historical process
where people were a bit
panicked or templates,
they were very new at the time.
When we can be specific?
Things get far more readable.
The version up there is slightly longer
but it says much more.
It says I'm going to
get an input iterator,
now, take any,
I'll compare and I'll take any other type
as long as it's in equality
comparable with the value type.
Let's see, I don't have a
definition of value type there
but that's the one that looks in and finds
what the value type is, the element type.
And with auto on type name,
we have to sort to read
the implementation again.
We are back to the sort of the
K&R style of C declarations.
There's simply not information enough.
And here, there's people
who look at the top
and says ooh, that's complicated.
It isn't really.
It's just unfamiliar for a day or two.
I know from students that on day two,
they wouldn't go back.
And so,
oops, I'm going the wrong way here.
Sequences are expressed
as pairs of iterators.
We are going to move forward.
We're going to get the
range TS so we can stay
instead of saying there's
a pair of iterators,
we can say I want a range
however I expressed.
and you get to the lower version there.
It's important that we are now moving
from the sort of language experimentation
to the supported use of things with,
with Standard Library support and such.
And by the way, don't
expect optimal readability
from older code that has been
bug compatibly moved forward
to use concepts.
And don't expect the most readability
to come from the deep foundation libraries
that has to have the ultimate flexibility.
I have observed that the most benefits
from readability actually
comes from relatively simple
and relatively new libraries
written by people who understand concepts
as opposed to people who
understood the old techniques
very well and replicates
them using concepts.
So, we still have a ways to go to overcome
the ways of old thinking
that leaves the complexity in place
expressed with the new facilities.
So again, back to this notion
that auto and type name
is the weakest form of typing,
simply says, it's a type.
In theory, we could actually do
without having auto in the language.
Look at that concept there, capital Auto.
That's the way we could do it.
If we started designing
C++ today, quite likely,
the building feature
auto could be eliminated.
And my aim is that you would accept the,
you can accept the concept
wherever auto is now.
That's backwards, I
really would like to use
the fully generic concept
auto if and only if
there isn't a more precise way of stating
what I'm trying to state,
and that's not so good.
And again, this has
been around for a while.
I proposed auto if auto in the
Standards Committee in 2003
and the screams of
horror were rather loud.
Basically, in C++, we
tend to rely on types
and find things like void star
and sort of suspect, it's a code smell.
And I think we will get to the point
where we think the type
name and auto will be,
well, a code smell.
If they're there, somebody
hasn't thought it through
that at least, if you have
an auto or type name T,
there will be required clause coming later
to see what it means but what
we really need to get away
from this fairly primitive thinking
that we take a type and
we do something with it
and we try and write
code that doesn't work
if you get something
that is slightly wrong
and anyway, it gets complicated.
So concept will change
the way we think about it.
It's not just a hope.
It's what I have observed again and again
with people who have
learnt this kind of stuff.
And this is not just support
for business as usual.
This is major, it
changes the way we think.
As I think I've said, I'm
not keen on individual
language feature talks and details talk.
This is not a detail.
This changes the foundation.
And the community as a whole
is going to be slow as usual.
It takes a long, long time
to get millions of people
to change their mind about anything.
And there are people who
will never change their mind
but individuals can do much better.
So, even if we can't
get everything we want
in all the code now,
maybe you can do better
in your local code and maybe
you can start experimenting
even if you can't deploy it yet.
That's what I and others
started doing a few years ago.
It works.
First experiment, figure out
what works in your context,
move on and it can get
to the production level.
GCC has pretty good support for concepts.
Clang is coming.
I believe Microsoft is
thinking hard about it
and it's not really that hard to implement
once you get going and the
Standard Library has it
so come C++20, we should all be there,
so, it's time to get ready.
Concepts weren't born yesterday.
There's a lot of people says oh, it's new.
It's dangerous,
or as opposed to oh, it's new, it's great
or it's new, it's really should
have been something else.
No, it shouldn't have been something else.
We have spent a lot of time
figuring out what fits with C++
and how to work it in,
how to work the details.
Alex started
in '81 called algebraic structures
and then he's been calling them concepts
since somewhere in the
'90s, early 90s, I think.
No, no, no, no, late '80s.
And I tried to find a way of
constraining templates in '88,
it failed and either I know anybody else
knew how to get all three
properties I wanted but we have.
The STL was specified in terms of concepts
even though there's no
language support for it,
it's a fundament way of
thinking and therefore,
it makes sense to talk about concepts
even if there's no language support for it
and then there's a lot of history here,
I'm not going to go into it.
That's a different talk but don't think
it's something new or
something totally malleable.
Good people have worked hard
on this stuff especially him.
Okay, I haven't actually said that much
about what is the concept
and now, I should.
And Alex said concepts
are all about semantics.
This might surprise you because
there's no semantic part
to the language support,
however, it says what kind of
properties a type must have,
how can you use it and
you have to think about
what that means, what makes sense.
So basically, technically,
concepts are compile-time predicates.
ForwardIterator, it's true.
If T is a forward iterator
and it is false otherwise.
And that can be used in the
language and the language rules,
that's the idea.
What a forward iterator is,
we can define somewhere else.
We know what it is, we can
look it up in the standard.
And concepts are fundamental.
They tend to represent fundamental
concepts of our domains.
So, if you're mathematically inclined,
you have concepts like
monoid, group, field and ring.
In C++, we have input
iterator, forward iterator,
by directional iterator,
random access operators.
These are there.
Today, we just have to represent them
with language support.
And so, we've always had concepts.
I mean, you read K&R C,
the first definition
says an integral type is,
an integer type is, these are concepts.
Now, today, we can actually
represent that in C++
but we've been using that
for some definition of we
for more than 40 years.
And we, yeah.
We have direct language support.
So this is philosophy, you have Plato.
This is the engineering.
Archimedes, he's an engineer.
And we must learn to
use the techniques well,
not just philosophy, but
actually practical use
and practical support.
And the concept is good if it represents
well-thought out concepts.
It is not the minimum requirement
for an implementation.
We've been spending
some time doing lifting
and trying to find the
absolute minimal requirements
of an algorithm.
That's not it.
The ability to add things
is not a fundamental thing
for a large group of things.
The ability to use ++ is not.
You need something more well-thought out
and there's no semantics
to an individual operation
but there is a semantics to
a combination of operations
like plus, minus, multiply and divide
and good concept should
support interoperability, so.
Hmm, okay.
A lot of people think about
concepts as types of types
and that's not it.
You don't do too much harm thinking
about a concept that
takes a single argument
as a type of a type,
however, most concepts
take more than one argument.
If you have a template that
takes two type arguments,
almost by definition, there
will be some relationship
between those two type arguments.
Why else are these type
arguments to the same function?
To same algorithm?
There has to be a relation.
You're using them in combination.
The second you have two arguments
of different types,
you need a concept with two arguments
and you're out of the type of type world.
The other thing is that,
like templates, concept
can take value arguments.
It's not that common just
now but since we now have
value arguments of different
types in C++17, 20,
it'll become more common.
So, some of the concepts
takes things like type and,
and the value.
This is not type of type kind of stuff
and if you look at it,
that means that concepts
are not type classes
and they were not meant
to be type classes.
It supposed to give you
implicit conversions,
mixed type operations which
people have insisted on
since Fortran and that type
theorists have disliked
since about the same time but this C++,
we have to serve the C++
styles of uses and C++ users,
they are not expressed in terms
of sets of functions either.
We tried that, it didn't scale.
So one thing to remember,
when you define concept,
when you think about concepts is describe
the concept for clusters of operations.
I mean, plus, minus, multiply, divide
and then you also probably
need plus equals, minus equals,
plus plus and such.
For stacks you have push and pop
and very rarely, does a concept
characterize a single operation.
HasPlus and HasMinus, a very
suspect when you see them
as concepts because
they don't actually work
in lots of places and they're
used for ad hoc combinations
of features and you
get a set of operations
that doesn't actually interoperate.
So, you have to think about that.
Here's a plug and play example.
I wrote a simple implementation of a sum.
Could have been accumulate or something
but I wrote it in terms of plus equals.
If I did the minimal dependency on that,
I would have a dependency
on a plus equals,
plus equal about, something like that.
The way of the dreaded ables.
If your types have an able at the end,
think a little bit harder.
There are useful concepts
that is named like that
but most of them aren't.
So, basically, when I
want assistant constraint
for that algorithm there,
I have to think how else
might I have expressed
that algorithm.
Should I just take plus equal
or should I take plus equals
and plus an equals?
Basically, some kind of
number is a better answer
because you actually also want
to have copyable unmovable,
things you didn't think about
just when you were doing it
so you want to express the concept,
you have to raise the level of discourse
to something that makes
sense basically in isolation.
What is it that I'm really relying on?
What is the fundamental
concept we're doing with here?
It's at least an additive
monoid but I would say a number.
And it's not just for algorithms.
Here's a piece of code which
happens to be a very minor
simplification of some real code.
It is an input channel,
it takes some transport
in a message decoder,
what's an input transport?
Well, as concept that says what it is
and what's a message decoder?
It's a concept that says what it is,
basically saying what you can do to them
and then you build up some context
and then you have a variadic,
variadic template here
that is used to initialize
the representation of the transport.
This kind of stuff is really quite hard
to write and understand and
it's really hard to explain to
new developers unless you have
the conceptual framework
provided by the concepts
that also gives you
the error handling if people misunderstand
this kind of stuff.
So you can build it, use
it for large frameworks.
Let's see, how do we define
concepts as Gabby Dos Reis
who work with me and
others for many, many years
for building up this kind of stuff.
If you are going to use some concepts,
obviously, the first choice
is to find some concepts
that somebody else has built.
I have a slide of some
sources but basically,
the range library and the
working, (audio cutting off)
standard has examples.
But if you have to build your own one,
the best thing to say is
that you can build it out
of existing things.
So, I talked about sortable.
A type T is sortable if it is a sequence
meaning it has begin an int,
has random access means
you can subscript an end
and this value type is comparable,
it has the operations there.
This is not brain surgery,
this is not rocket science
though I'm sure we're going to use this
in stuff for brain surgery and rockets.
But this really is just a notation
for the way we talk about these things.
We have hit some fundamental concepts
and it becomes easy to talk about them,
easy to write them down.
If you go down and want
to define some of the
simpler concepts directly instead of using
things done by others
like in every other area,
you get to more complication
and more trickery.
It's easy to say square root of two
but if you're going to
write a square root,
you have to know a little bit more.
Are you going to use Newton-Rapson
and how do you express it
and is it a better algorithm?
The minute you go down
one level of abstraction,
things gets more complicated,
sometimes, very much more
complicated but in here,
it's fairly simple.
I want a type to be equality comparable,
comparable to equals.
So there is a support in
a language core requires
which is that it can
specify what the properties
of expressions are.
So it says that if I have two Ts,
they have to be able to be
compared to equals and non-equals
and both cases, they
have to return a bool.
It's, again, not brain surgery
but it's more complicated than that
and you can get to slightly
more complicated things.
The way you get closer
to the core language,
you have to represent the facilities
of the core language a bit better.
So, a sequence requires,
it has to be a value type.
There has to be an iterator type
and those are things I've
just defined up there
and the must be a begin
that gives an iterator.
There it is.
There has to be an end
that gives an iterator
and the input iterator.
Sorry, the iterator of T
has to be an input iterator.
I mean it's not just returning
a type called an iterator.
We can actually say that
it is an iterator or else,
we're in trouble.
And by the way the value
type of the iterator
must be the value type of the type T
for comparable.
So that's one way of doing it,
now, we're getting down to the level.
We don't usually get much deeper than this
but if you look at the
definition of the range library,
you can see that as we go
down closer to the hardware,
to the trickier bits in the language,
they get more complicated.
If we can stay there at
the top slide, it's better
and if we can stay out of
this, it's even better.
Like our fundamental functions
like square root or sort,
we get them out of the library.
So do we get our concepts,
there are in the working
paper, a concept session
which has concepts so you
will never actually have to do
equality comparable and such
because they already understand it.
Ranges, just about anything
to do with algorithms
and iterators and there's
more places to find them.
One thing to remember is
I emphasize the importance
of complete concepts
with semantics make sense,
they are fundamental.
How do we get there?
Well, there are two things.
During development, we are
almost going to get it wrong
the first time.
So we need to get there somehow.
Secondly, sometimes, we
need building blocks.
So take an example here.
Here, is sort of an ad hoc thing.
It says that I requires
something that you can add.
That's usually a mistake.
I consider requires requires a code smell.
If you see that in your code,
you probably haven't thought hard enough
because usually when we want plus,
we also want these other properties
like you can get a+b but
you can also increment
and you can copy the thing.
And quite often, you want
to construct it from a zero,
things like that.
So if you write
some in terms of a concept,
you can improve it as you go along.
This kind of requires requires
which I've seen in far too much code
done by people who are
just coming to concepts.
They think, well, I know the syntax,
I can write these things.
Just show me the syntax and I'll write.
You get this kind of stuff
and these requirements
starts growing, growing, growing
and they don't lead to interoperability
because each operation is done by itself.
What you need to do is to
think about what can you do,
what can you build up.
Now, addable is one of
these dangerous addables.
So you think is it really what I want?
Don't I want a number?
Don't know what minus also?
If I don't want minus, why why don't I?
The minute you generalize
sum to accumulate,
you are up into the next
level of abstraction
where you need a proper
concept to constrain
what you can do.
There's been people complaining about
you can get accidental matches.
If we are calculating what
use of a concept matches
various concepts, you can get things
that could accidentally match.
Now, I picked here an
example from the old days
of object-oriented programming
where people were worried,
sometimes, reasonably.
Drawable, I define something drawable.
It's something that you can draw.
Be suspicious, it's
only got one operation.
It's unlikely to be a good concept.
It may be something we
did just at the beginning
of a development before we
knew exactly what it was
but be careful.
It's got a single
operation, it's called able.
Be careful.
Anyway, so, we make a shape.
It draws, we make a cowboy
because in the game industry,
we're drawers, and so
now, we can do a draw_all
and it draws all.
Now, this is all right if draw
really is the shape that draws.
It's not so all right if it pulls a gun.
So, that's an example from the early days
of object-oriented
programming just translated
into modern terminology.
Accidental matches can happen but really,
that's a bad concept.
It doesn't represent anything fundamental.
If you have done this properly,
there would have been draw
operations than just draw
and it's unlikely the cowboy
would have had them more.
Similarly, for a cowboy,
there would be operations
that probably didn't fit with
a shape like get on the horse.
So the accidental match can
happen, it definitely can.
Classic examples, input
iterator and forward iterator,
they only change in their semantics,
not in the set of operations.
And if they do, you can add
disambiguation operator.
I mean there's things
that forward iterator
can do that input iterator is cut
and I go back to the beginning operation
or something like that, it's not that hard
or you can use a trait class,
they still have their uses.
But beware of the single
constraint concepts.
Here's a thing that I found.
I started like a lot of people
with concept like a number
that had the four operations
and then I realize
that I needed that one then
I realized I needed that one
but notice one thing.
My initial concept, the
incomplete concept were useful.
It caught a lot of the errors
and allowed me to think
and I could just improve
it as we went along.
What's missing here?
The fact that numbers
can be copied and moved.
So I haven't quite gotten there yet
but you develop these once at a time.
Refinement of concepts in our
minds is a gradual process
as we learn and we improve our concepts.
They have names so we can do it.
One thing that concept do
not do concepts as designed
for C++, it does not catch all type errors
and template definitions.
This was a bit of a surprise to some of us
if you read the early papers.
It was one of the things we
wanted and we didn't get it
for a variety of reasons.
Here, do we really want
to catch this early?
So forward iterator and it does an add.
Yeah, it would be good if we could do it
but as a matter of fact,
doing that puts constraints
on the performance, puts
constraints on the compilation
feature and it actually constrains things
rather dramatically.
And this kind of error
will eventually be caught
but only at an instantiation time.
We're falling back to
the bad old techniques
for catching the errors.
On the other hand, it allows us to write
simple concepts simply and
so to have fast compilers
and be very, very flexible and I was,
look, I have a set of rules
that I'm following from the D&E,
expressed in the D&E book.
It's more important to
allow a useful feature
than to prevent every misuse.
So, why not?
Basically, we decided when
we started redesigning
concepts after
the C++0x debugger
that 90% of the benefit
came from use checking
and we figured out how to do use checking
with the current notion of syntax.
Gabby did some experiments.
We know how to do it, it's just
we got more and more worried
that we were wrong to
close the system like that.
How do I build these concepts up slowly
before I know all the constraints?
How about debug aids?
How about telemetry, logging?
If the concept has to be complete
so that you can check and catch everything
that you use in the implementation
that you didn't mention in the interface,
then you have a closed system
and you sort of have to
reach a level of perfection
before you can use it.
And since you don't
because perfection doesn't come early,
you keep changing the interface.
You want a debugger, you
have to add debug ability
in the interface.
You want statistics
added to an old concept,
you have to add that to the interface.
Now, all the user code might break.
So we were beginning to get
very worried about how you,
how you do a transition
from existing type of code
to new code, how do you
use old code from new code,
how do you use new code from old code,
we decided we are not going to touch
definition checking for now.
This actually requires
serious syncing, not just,
the fact that we know how
to do it is not sufficient.
Just because you can do something
doesn't mean you have to.
Okay.
You can still do sum checking
like static_assert.
I want to know if my type
matches the range concept,
there it is.
And for testing of my algorithms,
I can just build what's called archetypes,
sort of a class called, sort of X
that has all the properties
that I'm expecting
and you feed that into a
static_assert with my algorithm
and you see if it works.
The only snag is that
you are likely to make
the same mistakes when you are
defining the architect type
as you did when you defined the concepts.
This can be, of course,
mitigated by having different
people borrow from such
but just because there's no default,
definition shaking
doesn't mean that there's
no checking you can do
because, well, you can.
you can just see it there, this works.
Let's see.
We would like to use
high-level concepts more often.
And so here's sort of a first cut
on constraining merge.
Merge is standard,
standard algorithm
and it's one of the more complicated ones.
And so the first cut looks like this.
I need three types.
The first one is a forward iterator,
the second one is a forward iterator
and the third one is an output iterator.
And you have to be able
to compare and assign
to all of these things.
This is somewhat tedious and
that's what the standard says
but we are doing the
equivalent of sort of doing
primitive operations.
In real world, we've learned
we have to aggregate operations
into functions, classes and such.
And so this is a sort of,
it's easy to make a mistake
when you write so much
and it's hard to read.
One of my favorite
phrases, headache inducing
an accumulate is much worse.
And by the way, this
particular pattern appears,
I think, four times in the standard.
So what we do is we design mergeable
which is the concept
that requires three types
and then does or the checking.
So we need to get away from
the most simple-minded things
often starting out with
single type concepts
and then going into the
relationship between those concepts
and simply saying I want three types
and they have the proper
relationships among themselves
for being mergeable.
There's a more elegant
way for doing that thing.
I want to introduce three type names
and they should be mergeable
but that's not going to make it
into C++20 though that is my favorite
for expressing this idea
and basically, then you just
have to define mergeable
and merger boy is the one that
has the properties we require
and obviously,
this has the point,
this has the advantage
that if you write your code
like this,
you can improve that
concept as you go along
because the first time, you're
not going to get it right.
So, having it named is helpful,
having it in one place
to fix it is helpful.
It is just like when
you're defining functions.
You are defining functions.
So the principles of
concept design is basically
think harder about the semantics,
think harder about the
fundamental concepts.
This is why concepts are
core concepts, by the way
and you have to think
about what is universal
and what can be used in many
places as opposed to ad hoc.
If you find you write a
lot of similar concepts
and a lot of long complicated concepts
like if you see something like that,
it's like seeing a whole lot of statements
and a large function.
You should eventually
get used to thinking,
oh, that's a code smell.
Really, are there something
that you can abstract from it
and build something manageable like that.
And you can actually predict
that you're going to find in this case
so it's better to introduce
the name concept earlier
even if you can't do all the details yet.
Consistent set of properties,
you make them concrete by using concepts.
And basically, it's much
easier to think about your code
in terms of concepts.
Again and again, I have said,
met people says, well,
I couldn't even think of this
solution without concepts
just like I've met people
I can't even think about this
solution without classes.
It's a fundamental thing.
It helps thinking.
The major inspiration for a lot of this
is elements of programming by Stepanov
and McJones.
John Backus was in this picture, so.
It's interesting.
He was the one that did
Fortran, by the way.
So there's a certain continuity
in the programming world.
And so concrete suggestions.
Make sure that you can
think about the semantics.
When you see a concept, it's expressed
in terms of syntax part.
You should be able to think
about it as having semantics.
Eventually, we may get
support for doing that.
That will be called actions
but we are not there yet.
Incomplete concepts are
far better than no concepts
and basically, you tend to start thinking
then you have simple concepts
and let them grow later.
Use named concept.
Requires requires is code smell.
You can use static_asserts
to get upfront testing
of your types and your concepts.
To find the algorithms in
terms of general types,
the ideal is plug-and-play,
not absolute minimization.
And variables should be
constrained with concepts
so that you don't have to write
in a fully functional style
and you don't have to
fix the type too early
to be able to get readability.
This improves readability.
And basically, for those of you
who haven't already been using
concepts for months or years,
try them.
I have not personally met
anybody who tried them and
went back to on typed templates
because they wanted to.
It's quite often that you have to go back
because the implementations
are not universal yet
and code bases cannot always be updated
to the latest compiler
on all of this stuff
but I think it's much
better if you try concepts,
and so for withdrawal symptoms,
for years after till they
upgrade the compilers
and the code bases then you
don't think in terms of concept
because you will not go back.
Once you have used concept,
you think in terms of them
and it improves your code
even if you don't have
the language support.
This is the underlying argument for that
why this is fundamental
and part of the basic
structure of the language.
And basically, good interfaces
is key to good code.
You can do it now.
You simplify the code.
There's a lot of complicated
template metaprogramming
using enable_ifs that
gets radically simplified
by using concepts.
So if you're saying no, no,
I'm not doing generic programming,
I'm doing template metaprogramming.
There's two things wrong with this.
One is the word template.
Metaprogramming doesn't have
to be templates anymore.
A lot of constexpr
functions will help you.
Secondly, concepts help with
template metaprogramming
because not all of template
metaprogramming is on typed.
And basically, the thing
that people always say,
well, you want concepts
because it gives better error messages.
No, you want concepts because
of all of these other things
and as a side-effect, you
get better error messages
because you have expressed
your ideas more clearly
so that even a compiler
can understand you.
Okay, and of course, fewer errors.
That's what I have to say so questions?
(attendees applauding)
Do we have microphones?
No microphones?
You can probably yell loud
enough for me to hear it.
Yeah, so the question is that
he'd gotten the impression
that concepts were there
to express algebras
and my examples doesn't
seem to reflect that idea
and I don't think that idea is correct.
It's not just for math,
it's not just for things
you have an underlying theory about.
I showed mergeable which is
part of the standard library.
That's for an algorithm and I,
I showed the input channel example
where we're using concepts
to constrain and specify
actually the types that
take part of a composition
of things.
So this was why I said it's not just
for foundation libraries
which a slightly stronger statement.
I think it becomes very, very important
in application programming
and I've seen that again and again.
I've seen it in industrial code
and I've seen it very
often in student code.
- [Attendee] Bjarne, hi.
- Yeah.
- [Attendee] Can you go
back to slide 51 and just.
- That one?
- [Attendee] Yeah, can
you help me understand
what the error was that
didn't get caught and--
- Oh, an input iterator
does not support plus,
it only supports ++.
So, this is the kind of mistake
that you can actually see in real life.
Somebody had in their head the notion
that plus one and ++ was the
same thing but it so happens
that's not the way it's
defined because plus one
is to the type system,
basically the same as plus 20
and you don't want to be able
to go 20 steps into a list
because that is an ON operation.
And so we can't distinguish
plus one from plus 10
or plus 10,000 and so the
library is defined like that
and so this is the kind of mistake
that we would sort of like
to catch but the side effects
of catching that kind of thing
is to close the interface
around algorithms
so that you can't debug,
you can't have telemetry,
you can't have gradual evolution
and you can't have stable interfaces
so we decided that this one,
yeah, we'd like to do it but
we don't quite know how yet.
This takes more work.
I think now, if you want to say something,
please, find a microphone.
- Yeah, to go back to
the point about algebra
is I think in a more general sense,
you do have some examples
here like advanced
where you're using concepts to constrain
and to create an overload set.
You got advanced for forward iterators,
for max iterators but then
you have other examples
like sort which sort
takes something sortable,
merge takes something mergeable
and the standard swap
takes something swappable
and it's a little bit circular, right?
In that case where we become unclear,
well, is it sortable because I can pass it
to the standard sort or is
that a customization point
where someone with advanced,
you're using it as customization for it.
Someone could add their own advanced
that was less constrained
or more constrained
and it would pop into the
overload set at the right point
which sounds horrible but
like that's the functionality
you're enabling by like that
as opposed to if constexpr
or something like that
inside the implementation.
With sort, are we using it
as a customization point?
Is that the reason for
creating an overload set?
- Yeah, I'm supposed to repeat questions.
This one is a little bit hard to repeat
so I'll take it in chunks and repeat
if I don't get to the real point.
First of all, of course,
we can do it with algebras.
We've done the monoid,
ring, vector space stuff.
On the other hand, we
want the open the overload
that we get from C++
from the earliest days.
I want to say if
I want to specify the algorithms
in the standard library
with concepts and once you
have done them with concept,
you actually will be able to
add your own versions to it
just like you can add your own
versions through overloading
or if you're doing uptr and hierarchies,
you can add your own versions by,
by derivation.
Now, some people call that ad hoc
and definitely meant to support that also.
- In that case, with sort,
you're saying someone could
add a more constrained sort
that required sortable,
add also something else
and that sorta get picked.
- [Bjarne] Sure.
- Then you've got--
- I can use it both to get
a more constrained version.
So if you wanted a,
a sortable that also
could do foobar, whatever it is,
you would simply take
sort sortable and foobar
so you can so you can
get a narrower version,
a more constrained version as
opposed to what I was doing
was I was opening up.
How do you sort when you
don't have all the properties?
You can do both.
I'm going for flexibility here,
not for any particular theory.
And in some sense, any
concept in any algorithm
can be more or less constrained.
- Hi, so I've seen,
in the examples I've seen so far,
mostly just checks for
existence of operations on types
which is kind of something
that we haven't been able to do before
with enable_if itself.
Why does it stop there and I'll go further
and also check between
relations of operations.
For example, for the
concept equality comparable.
It's not just enough to check
that there is the
operation equal and unequal
but also they should return
the opposing value, right?
- I think there are two questions here.
The second one is why can't we check,
see semantic connections between
the functions of a concept.
We had a design for that for C++0x
called actions which I
actually think is very good
and could do exactly that.
It expresses relationship
between two operations
and we just didn't think
we could get that done now
and we didn't want to
complicate the process
through acceptance in
the Standards Committee
by throwing in more features.
- [Attendee] Is that is that
where contracts could come in?
I mean it's--
- No, no, contracts work on values.
Concept works on types and it
is still the type relations
between the operations that we
would be able to handle with,
with concepts and with actions.
There was a first part of your question
that I don't think I answered.
- [Attendee] I think I'm answered.
- Okay, thank you.
- I'm thinking about the
operators which like plus
unsigned integers which are not defined
for the whole range of the thing or
less than for floats, right?
And you have the concepts which would
cover these types
like sortable of floats, right?
Would you include float
to sortable or not?
I would definitely consider
floating point numbers sortable
and I would just ignore another number.
There's been long, long debates about this
and it relates to the
issue of whether concept
is a type of type.
It is not, it's a requirement
of what you get in
and as I pointed out repeatedly,
it may not be the complete
constraint of the implementation.
It is still the implementation's job
to know what is coming in case,
in case of a floating point number.
It might get an end
and it is the algorithm's job to decide
whether I'd wants to do anything about it.
- [Attendee] Right, but then,
you wouldn't have the same
semantics for example plus,
plus, it has not the same semantics
between instant loads, right?
So if you have a number, that
is not actually a number.
- Do you want me to also check, sorry.
Do you want me to also
check overflow and underflow
and everything?
- [Attendee] That is actually hard.
- It is very hard.
Checking for another number is very hard.
Checking for another number
only when you've got a floating point
type is very hard.
If I was going to work things
out for a small example,
I might consider it but I
think it's a wrong approach
for real-world scalable code.
- Yeah, but that would
result in incorrect code.
- Yeah, I mean it's a
great help for thinking
but I think trying to constrain
the concept used to the
things like that is wrong.
I have another argument
which I can't put my
mind to just now.
Another end number
was discussed again and again and again,
I suggest you go to Andrew
Sutton's talk about concepts.
He's really good at that point.
And isn't two hours into a presentation,
a little bit bool.
- Hi, a bit concerned about
the kind of coarse graining of concepts.
So you kind of address this preemptively.
You talked about how plusable
was not a good concept
and instead okay, we have numeric, right?
I think one of the
issues with this is that
in general, when you're designing
classes and especially for
application developers,
usually, it's better to error on the side
of a constrained interface.
So, if you don't need multiply
then you don't write it.
You can add it later if you need it.
If you think it's confusing or
misleading, you don't have it
so we're in the situation now where
if we start using this numeric constrain
in many, many algorithms
even though let's say
multiplications not used
like, I know accumulate probably
isn't actually done this way
but imagine if accumulate
was done using numeric then suddenly,
any type that I write that
doesn't have multiply,
we can't use with accumulate.
So it's actually kind of
encouraging people to like
slot themselves into these concepts
which it can be a good thing in some cases
but I think sometimes,
it's actually pretty bad
if you're basically saying to people,
if you wanna reuse this
high-quality generic code,
you better add like all
these other operations
you don't really need and
the algorithm doesn't need.
- This is a very old debate.
Should you have absolute minimal?
Yeah, I'm repeating the question.
Do you have absolute minimal requirements
or do you encourage people
to build more complete types?
And I don't think encouraging people
to think about where their
types fit semantically and
put some extra work in
is encouraging sloppiness
on the contrary.
You need to look at
experience and my experience
and other people's experience has been
that encouraging plug-and-play,
encouraging high level concepts
is useful and important.
Furthermore, I'm not
being rigid about that.
Notice I've said you evolve concepts
and furthermore, you can have
various levels of concepts.
It would be quite
reasonable to have concept
for things that could
be added and subtracted
but not having multiply.
So you have that flexibility.
When you see a new feature,
especially a major new feature,
I find the programmers are amazingly good
at imagining problems
and they are actually quite
often dramatically lousy
in imagining benefits.
And my view is that any language feature,
any concept if I've used upon
has benefits
and potential errors
and I consider software development
fundamentally an engineering discipline
as opposed to a branch of mathematics
or type theory.
And you have to evaluate the
benefits and the disadvantages
and then you have to observe
what happens in real life
and I have come to the conclusion
after watching this kind of
stuff and people using it
for a decade plus that
aiming for more general
and more complete concepts
actually improve code.
- Thank you.
- With introduction of templates,
we had to change syntax to allow things
to be constructed to move
syntaxes intended for classes.
So now we can use it for building types.
So we made changes to the
language to allow you to use it
more uniformly.
Now, with concept, I see similar danger.
The concept will be
easier to use obviously,
but it could be realized in multiple ways.
Do you see ways to change
the language to help us
realize the same concept in
multiple implementations?
- [Bjarne] I'm not sure I
understand the question.
- Imagine that we talk about sortability.
In future you might be able to use it
and do it using the
spaceship operator, always,
in the past, you'd collect
some other operators,
you'd still get the
same thing semantically
but it's really different implementation.
- So the point is that
you can implement things
in different ways and you
need some freedom to do this
and that relates to the previous question.
Do you define your
concepts really tightly?
Sortable is defined in terms
of begin end and less than,
that's what the standard says
and that's what the concepts is.
As a user, you then use
the name of the concept.
When the standard changes, say to,
say that sort uses the
spaceship operator directly,
your code will not change.
Your requirement on your users may change
and you may have to fix that
because the standard changed.
But there's this interaction
through the specification
of the concept that
gives you some freedom.
And that your argument is an argument
for the more general concepts
as opposed to the minimal constraints.
- I have a question about
the relevance of constexpr
in some of this.
I know within a function, for example,
if constexpr inside a template expansion
has different properties in some ways
than it does outside that,
how would the constexpr interact
with say function definitions
and concepts it requires?
- How would constexpr functions
relate to the way it's used in concepts.
There are currently not in concepts a way
of requiring constexpr
functions, for instance.
That is something we've
discussed over there.
- I was thinking more of trying
to express test properties
using constexpr instead of
forcing people to use enable_if.
Inside a function, that's very simple
but I was wondering if
there is a way to do that
with concepts as well.
- I would like to see
most users of enable_if disappear.
They are more error
prone, more complicated.
You sometimes have to have
both the enable_if of a condition,
the enable_if on the
opposite of the condition.
If you have two predicates,
you can end up with four
versions of the code.
This is horrid.
That's eliminated by concepts, that helps.
Constexpr functions fit with concepts
because they both ways
of expressing a predicate
if they return a bool.
So, it works together.
There's a few rough edges,
you cannot write a concept
this is require constexpr function.
By the way, code using
concepts compile faster
than code using workarounds.
Eric Niebler reported two weeks ago
that the range is TS that
exists in two versions,
and enable_if and the concepts
compile 25% faster with concepts.
Expressing things directly
is better than representing
things directly
and we are winning on all
accounts with concepts
or the previous work around.
- [Attendee] Thank you.
- [Attendee] Regarding the idea of SFINAE
and default template arguments,
template deduction.
What is the opportunity that exists
between concepts as it exists right now
and the ability for those to participate
in template template arguments
in deduction in SFINAE style
sort of errors that you may argue--
- As far,
okay, the question is how does
template template arguments
fit into it?
I think it they simply fit into it.
That is you can express
concepts that involve
template template argument.
Again, this is a level of
detail I don't want to go into
for the fundamental things but I think
you take the fundamental ideas,
the fundamental techniques
in the standard, you pull the crank
and out comes the right solution.
- [Attendee] Secondly, as well,
with the ever widening gap
between C and C++ for this matter,
there have been many features
that have been introduced
into the language over the
years that have allowed me
to do certain things that
I could still accomplish
and compile with the previous
generation of our language.
How do concepts and their
forward slash backwards
compatibility for that matter
restrict me from porting code
or using development environments
that exist in both C and C++?
- Can you use concepts
both with over compilers
and C and C++.
Well, concepts are not actually meant
to be backwards compatible, obviously.
And I think C is totally lost in this game
because it doesn't have proper generics.
You can't have concepts
as a language feature
supporting macros
and in practical terms,
if you need to work
on a variety of environment, some are old,
you can use things like
if you restrict yourself to
explicit requires clauses,
you can have requires macro
that turns into nothing
for old environments
and so what you can do is
you can develop your stuff
on a modern compiler, get
the checking in place,
do the testing on a modern compiler
then throw the switch so that
the concept becomes nothing
and if you have not used overloading,
it'll then run correctly on old compilers.
A slightly more subtle version of that
is to define your actual concepts
as sort of capital concept macros
that either turn into the
concept for the modern compiler
or to type name on the old one.
Again, you lose overloading but
you can compile with GCC 4.2
if you're unhappy enough.
It's pretty horrid but there are ways
of using older code, older compilers
but you have to do nasty things.
- [Attendee] And will there
be the ability to default
or specialize on concepts
within template programming?
- Will be thing for
default and specializing?
Specializing, yes, defaulting,
I don't quite understand
what it means in particular, so probably--
- [Attendee] Some other time, I suppose.
- Some other time.
- [Attendee] Hello, I'm coming actually
from a theoretical physics background
and I'm very excited
about these developments
that you've just talked about.
I have a question much of or some of what
can be achieved very
elegantly with concepts
has previously been
achieved by inheritance
through abstract classes and I wonder
what is this take that you have
on the position of abstract
classes in the future
with concepts being present?
- So, abstract classes versus concepts.
One thing I failed to emphasize
because I've become so used to it,
concepts is a zero overhead feature.
There is no runtime cost.
And that's important and I
should have said so and I,
once you get used to things, you forget.
So for abstract classes,
if you want a binary compatible
upgradable interface,
abstract classes are unbeatable.
Just a set of functions.
On the other hand, it causes
indirect function call
that can be expensive.
Concepts are the opposite.
There's no overhead,
they're just compile time predicates.
On the other hand, since
concepts are independent
of the layout of the
objects they manipulate,
they don't actually address the issue
of binary compatibility at all.
So I think they're sort of neutral.
You have to orthogonal chores.
- [Attendee] Okay, thank you.
- [Moderator] So we do have
12 minutes till the next talk.
If you would like to ask Bjarne question,
maybe come to the stage
and talk to him over there.
We'll get to the next talk here.
- Okay, thank you.
- [Attendee] Thank you.
(attendees applauding)
