Back to Index

Bjarne Stroustrup: C++ Concepts - Constraints on Template Parameters


Transcript

And then there's this idea of concepts that puts some... Now I've never even... I don't know if it was ever available in any form, but it puts some constraints on the stuff you can parameterize, essentially. Let me try and explain. Yes. So, yes, it wasn't there 10 years ago.

We have had versions of it that actually work for the last 4 or 5 years. It was a design by Gabby Dos Reis, Andrew Sautin, and me. We were professors and postdocs in Texas at the time. And the implementation by Andrew Sautin has been available for that time. And it is part of C++20.

And there's a standard library that uses it. So this is becoming really very real. It's available in Clang and GCC, GCC for a couple of years, and I believe Microsoft is soon going to do it. We expect all of C++20 to be available, in all the major compilers in 20.

But this kind of stuff is available now. I'm just saying that because otherwise people might think I was talking about science fiction. And so what I'm going to say is concrete. This is real. You can run it today. And there's production uses of it. So the basic idea is that when you have a generic component, like a sort function, the sort function will require at least two parameters.

One, a data structure with a given type and a comparison criteria. And these things are related. Obviously, you can't compare things if you don't know what the type of things you compare. And so you want to be able to say, "I'm going to sort something, and it is to be sortable." What does it mean to be sortable?

You look it up in the standard. It has to be a sequence with a beginning and an end. There has to be random access to that sequence. And the element types have to be comparable. Which means less than operator can operate on them. Yes. Less than logical operator can operate on them.

So basically what concepts are, they're compile-time predicates. They're predicates you can ask, "Are you a sequence?" "Yes, I have a begin and end." "Are you a random access sequence?" "Yes, I have subscripting and plus." "Is your element type something that has a less than?" "Yes, I have a less than." So basically that's the system.

And so instead of saying, "I will take a parameter of any type," it'll say, "I'll take something that's sortable." And it's well-defined. And so we say, "Okay, you can sort with less than. I don't want less than. I want greater than or something I invent." So you have two parameters, the sortable thing and the comparison criteria.

And the comparison criteria will say, "Well, you can write it saying it should operate on the element type and it has the comparison operations." So that's simply the fundamental thing. It's compile-time predicates. Do you have the properties I need? So it specifies the requirements of the code on the parameters that it gets.

It's very similar to types, actually. But operating in the space of concepts. Concepts. The word "concept" was used by Alex Stepanov, who is sort of the father of generic programming in the context of C++. There's other places that use that word, but the way we call generic programming is Alex's.

And he called them concepts because he said they're the sort of the fundamental concepts of an area. So they should be called concepts. And we've had concepts all the time. If you look at the K&R book about C, C has arithmetic types and it has integral types. It says so in the book.

And then it lists what they are and they have certain properties. The difference today is that we can actually write a concept that will ask a type, "Are you an integral type?" Do you have the properties necessary to be an integral type? Do you have plus, minus, divide, and such?

So maybe the story of concepts, because I thought it might be part of C++11. C-O-X, whatever it was at the time. What was the... Why didn't it... We'll talk a little bit about this fascinating process of standards, because I think it's really interesting for people. It's interesting for me.

Why did it take so long? What shapes did the idea of concepts take? What were the challenges? Back in '87 or thereabouts. 1987? In 1987 or thereabouts, when I was designing templates, obviously I wanted to express the notion of what is required by a template of its arguments. And so I looked at this.

And basically for templates, I wanted three properties. I wanted it to be very flexible. It had to be able to express things I couldn't imagine, because I know I can't imagine everything, and I've been suffering from languages that try to constrain you to only do what the designer thought good.

Didn't want to do that. Secondly, it had to run faster, as fast or faster than handwritten code. So basically, if I have a vector of T and I take a vector of char, it should run as fast as you build a vector of char yourself without parameterization. And thirdly, I wanted to be able to express the constraints of the arguments, have proper type checking of the interfaces.

And neither I nor anybody else at the time knew how to get all three. And I thought for C++, I must have the two first. Otherwise, it's not C++. And it bothered me for another couple of decades that I couldn't solve the third one. I mean, I was the one that put function argument type checking into C.

I know the value of good interfaces. I didn't invent that idea. It's very common. But I did it. And I wanted to do the same for templates, of course, and I couldn't. So it bothered me. Then we tried again, 2002, 2003. Gabi Des Reys and I started analyzing the problem, explained possible solutions.

It was not a complete design. A group in University of Indiana, an old friend of mine, they started a project at Indiana. And we thought we could get a good system of concepts in another two or three years. That would have made C++ 11 to C++ 06 or 07.

Well, it turns out that I think we got a lot of the fundamental ideas wrong. They were too conventional. They didn't quite fit C++ in my opinion. Didn't serve implicit conversions very well. It didn't serve mixed type arithmetic, mixed type computations very well. A lot of stuff came out of the functional community.

And that community didn't deal with multiple types in the same way as C++ does. Had more constraints on what you could express. And didn't have the draconian performance requirements. And basically, we tried. We tried very hard. We had some successes, but it just in the end wasn't... Didn't compile fast enough, was too hard to use, and didn't run fast enough unless you had optimizers that was beyond the state of the art.

They still are. So we had to do something else. Basically, it was the idea that a set of parameters defines a set of operations, and you go through an indirection table just like for virtual functions, and then you try to optimize the indirection away to get performance. And we just couldn't do all of that.