In 30 seconds
In a 2019 conversation with Lex Fridman, C++ creator Bjarne Stroustrup traces forty years of design choices back to a handful of constraints: performance, reliability, and code that still runs after a decade in a telephone switch. He argues that safety is a property of whole systems, that the zero-overhead principle is not a slogan but a filter for features, and that machine-learning accuracy good enough for prescreening is not good enough for the machines that must not ask a sleeping driver for help.
- 1Simula taught him that you can actually modularize very nicely, so a program's problems grow with its size instead of with the square of its size.
- 2A more practical warning: mess up the cache and performance drops by a factor of a hundred, a penalty that does not care which language you wrote.
- 3Watching modern C++ compile down to assembler, he says the code that looked like C became uglier and larger once it was machine code.
- 4Express the idea directly and, lo and behold, it runs faster — which means fewer machines, and less hardware that can break.
- 5He happens to dislike monocultures: whoever owns one can go stale, with every incentive to block change and none to innovate.
- 6An AI that hands a sleeping driver three seconds to recover is, in his view, the wrong architecture: he would rather never rely on the human in a reactor or a car.
Read the full transcript by chapter
When you press the brake pedal, there is no mechanical link to the pads
When you press a car's brake pedal, Stroustrup reminds listeners, the pedal is not connected directly to anything mechanical. A computer sits in between, and that computer had better work. A great deal of the code on machines like that is written in a language he started in 1979. C++ still underpins critical backend pieces of YouTube, and the same is true at Google, Facebook, Amazon, Twitter, most Microsoft applications, most database systems, and the physical systems that actually move in the world: cars, robots, rockets.
Lex Fridman, who has written hundreds of thousands of lines of C++ himself, brought Stroustrup onto the Artificial Intelligence Podcast in 2019. The conversation does not linger on syntax. It is closer to a man explaining forty years of tradeoffs: what has to be fast, what has to be reliable, and what he would rather not put in the language at all.
The first program Stroustrup remembers writing was a second-year university assignment. He calculated the shape of an ellipse, connected points on the perimeter, and printed star patterns on a paper printer. He learned to program late, in that second year. The first language he fell in love with was Algol 60. Fortran did not take. Pascal did not take. "It always got in the way of me."
Pascal's type system got in the way; Simula's stricter one helped
Assembler was more fun. From there he spent serious time in BCPL, the first work that actually paid — it covered his master's. Then he found Simula, an extension of Algol 60 built primarily for simulation. Along the way its designers invented object-oriented programming, inheritance, and runtime polymorphism. Simula was the language that taught him a program's problems could grow with its size instead of with the square of its size. "You can actually modularize very nicely. And that was a surprise to me."
A second surprise concerned types. Pascal's type system got in his way constantly. Simula's was stricter than Pascal's, and it helped. The lesson stuck as a design belief for C++: you need a strong type system to organize code, and it has to be extensible and flexible. Pascal, as he remembers it, was roughly the simplest language Niklaus Wirth could define for the needs Wirth had at the time. It carried "a highly moral tone": if you can say it in Pascal, it is good; if you cannot, it is not.
Simula, by contrast, lets you build your own type system instead of squeezing yourself into Wirth's world. What you actually build is a set of types and relations among types that let you describe an application — close to the original idea of a domain-specific language. Every language that uses the word *class* for type is a descendant of Simula, directly or indirectly. Its designers were mathematicians. They did not think in types; they understood sets and classes of elements, so they called the type a class.
A fire engine turning left is why inheritance exists
People often pick up class hierarchies and virtual functions and miss what Stroustrup considers more fundamental: you want to write a program with *your* types, under *your* hardware and environmental constraints. Hierarchies matter when types are related and you do not want them all independent. The classic case is a collection of vehicles. If it is a bicycle, write the code for turning a bicycle left; if it is a car, turn the car way; if it is a fire engine, turn the fire-engine way. You end up with giant case statements and if nests.
With inheritance you tell the base class — the vehicle — to turn left the way it wants to. Each concrete type already knows how. This was not a textbook cartoon. In the 1960s, he says, people used it to simulate and optimize emergency services somewhere. You needed inheritance and runtime polymorphism because you wanted to handle a set of vehicles in a manageable way, and you cannot rewrite the dispatch code every time a new kind of vehicle comes along.
Fridman names Lisp as one of his own favorites. Stroustrup is honest: Lisp is important and interesting, and lots of people write code in it, then rewrite it in C or C++ when they want to go to production. His world is constrained by performance, reliability, deployability, and the cost of hardware. He does not like things to be too dynamic. How likely is it that you can write a piece of code that is both perfect and flexible, small enough for a modest computer, and still put it in a telephone switch in Bogotá? If that switch fails late Sunday night and you find yourself in the debugger, the chance a programmer is standing there is zero.
A professional should know five languages; the second one matters most
Stroustrup has said a professional programmer should know at least five languages. He reckons he has known about twenty-five. In the era when he did C++, languages were smaller and you did not have to swallow a whole programming environment to learn one. The number that matters is not one. A person who speaks only one language, like a person who has lived in only one culture, is likely to think their own way of living is the only one. The second language is the one that most changes how you think. After that, five is not that long a walk.
Pressed to pick, he reaches for kinds rather than a canon. It is still good to know machine code, even today, when C++ optimizers write it. He does not think he could appreciate those optimizers without understanding machine code and machine architecture. The practical reason is brutal: mess up the cache and you are off in performance by a factor of a hundred. If you care about speed or about the size of the computer you must deploy onto, you cannot ignore that, and the penalty does not belong to any one language.
Going low-level is no longer what gives you performance. What does is expressing the idea so cleanly that you can think about it and the optimizer can see what you are up to. His favorite optimization these days is to throw out the clever bits and see if it still runs fast. Sometimes it runs faster. So he needs abstraction mechanisms, something like C++, to write compact high-performance code. Then add a functional language — Haskell or ML, he does not care which — for strict types and clear mathematics, and one language for churning something out quickly: JavaScript, Python, or Ruby.
The C-like code compiled into something uglier and larger
He points to a CppCon keynote by Jason Turner, who set out to program Pong on a Motorola 6800. Turner argued the machine still looks like a microcontroller: specialized hardware, very little memory, relatively slow. In real time he started from fairly straightforward low-level code, raised the abstraction level with C++11, and watched Compiler Explorer translate it into assembler. Even if you cannot read the assembly, you can see the effect. The source gets cleaner and easier to maintain. It shrinks, and it keeps shrinking.
Stroustrup says he could not, in any reasonable amount of time, hand-write assembler as good as the compiler generated from really quite nice modern C++. He goes further: the thing that looked like C was significantly uglier and larger once it became machine code. The demonstration pulls abstraction back from taste into engineering. Only abstractions the optimizer can actually see earn their place.
He is careful about what a tool is for. When you build one, you do not know how it will be used; you try to stop people cutting their fingers off, but you have no real control. He is proud of some of the ways C++ is used and wishes people would not do others. Bitcoin mining is his favorite example: it uses as much energy as Switzerland and mostly serves criminals. JavaScript in the browser, for all its ugliness, enabled a lot of things more "principled" designs failed to ship, because non-professional programmers could not understand those designs. "Let's not be bigots here."
Safety, performance, and security are properties of the whole system
C++ exists, in his telling, for people who want to use hardware really well and then manage the complexity of doing that through abstraction. Reliability is not a side quest. He wants telephone calls to get through with the voice intact. A bank must not crash; it might be your account in trouble. Games can crash and nobody dies. The combination he cares about is performance as an enabler of dependability: if you spend all your time interpreting a simple function call, you will not have time for the signal processing that makes the call sound right — or you will need ten times as many computers, and nobody can afford the phone.
Modern cars already carry millions of lines of code; driver-assistance and autonomy add more. Fridman has talked to regulators who are extremely nervous about software that can make fatal decisions. Stroustrup's first move is to refuse a common trap. Safety, like performance and like security, is a systems property. People look at one part of a system and say that piece is secure. If you actually want reliability, performance, or security, you have to look at the whole system. Making an individual part perfect may not even be the best path to the highest reliability.
C++ is not type-safe; you can break it. His reply is blunt. He can break anything that runs on a computer. If he wanted into your machine, he probably would not go through your type system. He would try a SQL injection. Human beings in the loop make proving a given level of safety hopeless fairly quickly. So he pulls the conversation back to something you can make progress on: C++ programs that crash less often, largely by simplification.
Ugly code is easier to recognize than beautiful code
The first step is not a test framework and not runtime checking of everything, though those come later. It is less code, code less likely to go wrong. If the way you think about a problem is not in the code, there is a missing piece that lives only in your head. Readers can see what the program does; they cannot see what you thought unless you expressed it directly. When you do, the code is maintainable, easier to debug, easier to change, and easier to test. "And lo and behold, it runs faster." You can use fewer computers, which means less hardware that can break. He reaches for Einstein: as simple as possible, and no simpler.
Fridman asks the obvious question: if someone handed you the Ford F-150's code, could you tell whether it was a mess? An experienced developer can smell it, Stroustrup says, looking for tricks that correlate with bugs elsewhere. He has tried to write down what good code looks like; the current version is the C++ Core Guidelines. Rules like that do not create good programmers by themselves, but they limit the damage and the chance of mistakes. You can chisel every rule of sculpture into marble and still not get Michelangelo. A good program, like a serious work of art, needs a spark of inspiration, and you cannot regulate that. You can only approach the craftsmanship.
It is easier to recognize ugly than to recognize beauty, because beauty sometimes comes from something innovative and unusual, and you have to think to appreciate it. Messes have things in common. Static and dynamic checkers can catch a large number of the most common mistakes. He is a particular fan of static analysis, because it can check not just language rules but usage. You represent the code so a program can walk it and look for what is right and not right. Resource leaks are a favorite example. At C level, a `malloc` must have a matching `free`. In one function that is often easy. Once the pointer escapes the function, the problem is almost unsolvable.
Run a static analyzer and you will find some leaks and quite a few places where the analysis cannot be complete — runtime dependence, the cleverness of the analyzer, or simply time. Combine the analyzer with a set of usage rules and you can see why the rules were violated, which keeps you out of the impossible cases. You do not want to solve the whole problem. At its best the analyzer is an educational tool: it guides how you write future code. He would much rather find errors before the program runs. Once it is running, a failure means you need an error handler, and error-handling code is among the hardest to write, because you usually do not know exactly what went wrong. How do you recover when you do not know the problem?
If you have an abstraction, it should cost nothing extra
There is a tension between high-level abstraction and efficiency, Fridman says. Stroustrup agrees, then flips it: you often get the best efficiency out of the best abstraction. Abstraction is his main tool for performance. He also wants a correction on the record. People always quote him as saying C++ is an object-oriented language. He says he never did. He said C++ *supports* object-oriented programming, and other techniques. The best solution to most complex, interesting problems needs ideas from object orientation, data abstraction, functional style, and ordinary C-style code.
You cannot just add features. If you add what looks pretty, or what people ask for, one by one, you will not get a coherent whole. You need guidelines that decide what goes in, what stays out, and how a feature must be modified before it can enter. Most of those rules are not language-technical. "Don't violate the static type system" is there because he wants things reliable on reasonable amounts of hardware. One of the rules is the zero-overhead principle: if you have an abstraction, it should not cost anything compared with writing the equivalent code at a lower level.
A matrix multiply written with a matrix abstraction should be such that dropping to arrays and pointers does not make it faster. People have written such multiplies and gotten code that ran faster than Fortran, because the right abstraction lets the compiler eliminate temporaries and do loop fusion — work that is painful by hand in a low-level language. After talks demonstrating that, a common question is: if I rewrote your C++ in C, how much faster would it run? The answer is much slower. The first time he saw the effect was in the 1980s, with Doug McIlroy. You should only beat the abstraction once you drop into assembler for a part of the machine the compiler does not know about.
Four front ends exist because he hates monocultures
The magic of zero overhead, he says, does not live in one place. You need programming technique, language features, and implementation techniques together. Brian Kernighan pointed out that one reason C++ succeeded was craftsmanship Stroustrup put into the early compilers; he also designed the language and wrote a fair amount of code with it. Nobody has all the skills required in every field where C++ is used. Concurrent programming alone asks for concurrent algorithms, the details of lock-free code, compiler techniques, and an application area such as graphics or web servers. That does not fit in one head. Two or three people can hold it.
Why so many implementations? Nobody decided there should be. When he designed C++, the world was not one x86 box running Linux. IBM, Sun, Motorola — companies had their own compilation structures. He wrote a front end assuming others would write compilers if the language succeeded, and he wanted to use the backend infrastructure already there. His users were on twenty-five different linkers. He could have written a linker. He could not write twenty-five and still work on the language. Today there are at least four front ends: Clang, GCC, Microsoft, and EDG, which supplies a large share of independent organizations and the embedded industry. Backends are beyond counting, because embedded architectures differ too much for a single implementation to have been an option.
He also happens to dislike monocultures. They are dangerous because whoever owns one can go stale. There is no competition, no incentive to innovate, and a lot of incentive to put barriers in the way of change, because they already own a comfortable world. GCC was great, then it got somewhat stale; Clang came along and GCC is much better now. Microsoft is much better now. A small number of front ends puts pressure on standards compliance, performance, error messages, and compile-time speed. Asked for his personally most beautiful feature, he does not reach for something new. Constructors and destructors. A constructor establishes the environment for using an object; a destructor cleans up the messes at the end. That is why C++ need not depend on garbage collection, and why it can deliver predictable performance.
The idea is better known as RAII — resource acquisition is initialization — which he offers as proof he should not be in advertising. "I get the best idea and I call it resource acquisition is initialization." Constructors and destructors went into what was then called C with Classes around the second week of work in 1979. Once you control birth and death, you must control copying, because a copy is another way of creating an object. Then you must be able to move things, so you get move operations. That set of operations is how a C++ type is allowed to exist. Fridman lifts it toward philosophy: creating an instance and releasing its resources is like birth and death defining a life. Stroustrup takes the step. Philosophy matters. You cannot do good language design without it, because what you are determining is what people can express and how.
Concepts took thirty years because flexibility and speed were not optional
Templates are how C++ does generic programming. The compiler looks at the template and the argument, combines them, and generates code as if you had handwritten a vector of that specific type — the minimum overhead. With several parameters it can combine information the compiler would not usually see at once, and sometimes run faster than clever handwritten code. The cost is debugging. Optimized C++ has always been ugly: even the 1983 Cfront already had front-end and back-end optimizations, and the C it emitted was meant for machines, not humans. Templates make it worse, because the optimizer drags information from different parts of the program together. In practice you turn the optimizer off to debug the first level, then turn it back on.
In 1987 or so, designing templates, he wanted three properties. Extreme flexibility: it had to express things he could not imagine, because he had suffered languages that only let you do what the designer thought good. It had to run as fast as handwritten code, or faster — a `vector<char>` as fast as a vector you built without parameterization. And he wanted to express the constraints a template places on its arguments, the way a function prototype states an interface. Neither he nor anyone else knew how to get all three. For C++, he had to have the first two; otherwise it was not C++. The third bothered him for a couple of decades. He had been the one who put function-argument type checking into C. He knew what a good interface was worth.
Around 2003, Gabriel Dos Reis and he started analyzing the problem; a group at Indiana University started a project. They thought two or three years would do it, which would have made C++11 into C++06 or 07. The core ideas were too conventional. They did not fit C++ well: weak on implicit conversions, weak on mixed-type arithmetic. A lot came from the functional community, which did not deal with multiple types the way C++ does, had more limits on what you could express, and did not have the draconian performance requirements. It did not compile fast enough, was too hard to use, and did not run fast enough unless you had optimizers beyond the state of the art — they still are.
The version that landed is compile-time predicates. You can ask a type: are you a sequence? Do you have begin and end? Are you a random-access sequence? Do you have subscripting? Does your element type have less-than? Instead of "I will take a parameter of any type," a template can say it will take something sortable, and that is well defined. The word *concept* is Alex Stepanov's, the father of generic programming in the C++ sense: these are the fundamental concepts of an area, so they should be called concepts. By 2019 the design — with Dos Reis, Andrew Sutton, and Stroustrup — had been available in GCC and Clang for years, was heading into Microsoft's compiler, and was part of C++20, including the standard library.
Standardization is like democracy, except you have to ship on a three-year cycle
Sometime in the early 1980s, someone from IBM and someone from HP walked into his office and told him they wanted to standardize C++. He said it was not finished and not ready. They said he had not gotten it. Their organizations depended on C++; they could not depend on something owned by another corporation that might be a competitor. They could rely on him, but he might get run over by a bus. They were going to standardize it under formal rules, and he really wanted to be part of it, because otherwise they would do it themselves and they knew he could do it better. Through arm-twisting and flattery, a first meeting convened in late 1989: about twenty-five people, apparently a record for that kind of session, lectured on ANSI rules.
The process is open. You pay a fee — then about a thousand dollars, by 2019 about a hundred and eighty — and you can sit in the room. They meet three times a year, a week at a time, hold technical discussions, and vote. Votes are one per organization, so IBM cannot walk in with ten people and dominate. He thinks most people would agree that is better than if he decided, or if a single organization like AT&T decided. Standardization is not pleasant. It is horrifying. It is like democracy. Churchill's line applies: the worst way, except for the others.
The first technical meeting was in 1990; they voted the first standard out in 1998, in eight years rather than the traditional ISO ten, with all countries in favor. They thought the next one would take six because now they were good at it. It took thirteen. Second-system effect: they knew how to do it, so they got more ambitious. And on a long cycle, just before you ship, somebody has a bright idea that really must go in. They did that successfully with the STL, which he thinks saved C++. Later attempts were less dramatic and took longer. After C++11 they decided to ship more often. Slip a ten-year cycle and you notice when eleven years have passed. Slip a three-year cycle and you get the feature back in three or four. Fourteen shipped on time, seventeen on time, and he was confident twenty would too.
Predictability matters to implementers because they have a target and they hit it. After 11, most compilers took two years to be good enough. By 14 they were much better. By 17 almost everybody shipped in 17. For 20, most implementations were already delivering in 2019. The language itself, at the highest level, is for people who want to use hardware really well and manage that complexity through abstraction: a C-like machine-facing layer, and then the class as a user-defined type you should be able to use like a built-in — an integer that cannot overflow, for a real marine diesel fuel-injection system, or a matrix, or a gnome in a game — with inheritance for related types and templates for the other kind of similarity, the `vector` of anything.
Principles can be shared; the tradeoffs cannot
Fridman asks whether language design might one day rest on a unified set of principles, the way physics hunts for a unified theory. Stroustrup puts the ages on the table. Modern physics, counting from Galileo, has had about seven hundred years. Modern computing, from around 1949, has had seventy — a factor of ten — and physicists are not as bothered with users as language designers have to be, because programming is done by humans. The field is immature. At a philosophical level you can still agree on principles: do not leak resources; do not handle at runtime errors you do not need to handle; do not violate the type system you chose. Type systems can differ; once you have one, do not break it.
He once sat with designers of Common Lisp to talk about languages and features. At first they agreed on nothing; he even joked about the parentheses. Then something clicked. He described a problem and the principles he used to solve it; they had faced a different problem and used the same kind of principles. They walked through large chunks of C++ and of Common Lisp and found similar reasoning underneath. The constraints and the intended users were very different. Principles can be shared. The tradeoffs cannot. He already sees convergence in the wild: RAII, which C++ adopted early, shows up decades later in garbage-collected languages as disposal patterns, because those languages are trying not to rely on GC for everything. He notes that Rust has adopted RAII, and that some people accuse him of having borrowed it twenty years before they discovered it.
Machine learning, Fridman suggests, is another way of building something that takes input and produces output — fuzzy, noisy, like a person. Stroustrup does not hedge: it is a different world, and in his domain he does not like fuzziness. People say they want everybody to be able to program. He does not want everybody programming his airplane controllers or car controls. He wants that done by engineers, specifically educated and trained. C++ is a sharp, effective tool for professionals who aim at precision. You do not put people who do not understand math in charge of calculations. Counting on your fingers will not get you to the moon. Fridman mentions 99.8 percent accuracy. What Stroustrup has seen is more like 84 percent, and with a lot of blood, sweat, and tears you can get to 92.5. That is fine for prescreening before a human looks. It is not good enough for life-threatening situations.
The design that scares him most is the one where the neural net, faced with something too complicated, asks a human for help — and that human is reading a book or asleep, with thirty seconds or three to figure out what the AI could not handle and do the right thing. The handoff cost between machine and human is very hard to account for. Stating the problem is not the same as calling it impossible. His preference is simply not to rely on the human. Driving a nuclear reactor or an autonomous vehicle, it is much better to design systems that never ask a person for help. All of that software, he adds, still sits on top of C++, which is why he keeps a weather eye on the field even though he will never be an expert in it. No major system today is written in one language, and there are good reasons for that.
More from this channel
3:36:56EN
5:15:51EN
3:12:58EN
3:46:18EN
3:51:47EN
2:53:42EN