Effective Modern C++
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Meyers, Scott
Scott Meyers’ Effective C++ books are renowned for their clear, insightful descriptions of how to get the most out of C++, but they were written for C++98—for “old” C++. “New” C++ is defined by the C++11 and nascent C++14 standards, and Scott Meyers’ forthcoming Effective Modern C++ is devoted to the effective use of features in C++11 and C++14. For this presentation, Scott will select a few guidelines from Effective Modern C++ and walk you through them. The guidelines will focus on specific practices in C++98 that require revision for the most effective use of the modern versions of C++.
 Well, good morning. I'm Scott Myers. I thought I'd be talking a little bit about C++ today. I am standing in the only location I can find on the entire stage where I could actually see you. This is relevant because if you want to ask questions and you raise your hand, I'm probably not going to be able to see you. So this is what we're going to do. Normally, what I tell people is if they don't understand something or if they're a little concerned, they should ask a question, which I want to encourage. If you feel uncomfortable asking a question, what I normally encourage people to do is to look really confused and to stare directly at me. But that's not going to work because I have lights in my eyes I won't be able to see you. So you have two choices. One of them is you can make lots of motion like this because I think like insects, I can in fact detect motion. So that's one possibility. Or if you see me, go to the only location on the stage where I can see you, that might help. Or you can start making really uncomfortable sounds as if there's just something terribly wrong and then I'll try and figure out what's going on. So that's the plan for questions and answers. Also, if you don't want me to see you, you need to encourage me to go any place except that one location on the stage. I want to talk today about effective modern C++, which is the latest project I've been working on to try to put together guidelines for how to make the most effective use of the language. And in this particular case, when I talk about the language, I'm talking about the new features in C++ 11 and C++ 14. So for purposes of the discussion today, I'm assuming you already know everything in C++ 98. That's old stuff. So the question is, how do you make effective use of the newer stuff? And the outline that I have for the work that I've been doing is to break down the general topic areas in C++ 11, C++ 14. This is what I've broken them down into. When I started the project, I did not plan to talk about type deduction at all. I don't like talking about type deduction. It's kind of a dull topic. I found out I was unable to discuss C++ 11 and especially C++ 14 without explaining how type deduction works because type deduction is widespread in C++ 11. It is essentially much more widespread in C++ 14. That's a whole separate talk. In fact, I'll be giving a talk on that topic this afternoon at 4.20. So that's a different talk. The other topic areas that I have been investigating, putting together guidelines is auto, gets its own chapter, a whole bunch of information on changing the habits you have from C++ 98 to moving to C++ 11 and C++ 14, different ways of doing things. Chapter on smart pointers, chapter on R value references, chapter on lambdas, chapter on the concurrency API. The only things I'm going to be talking about today because we have a limited amount of time is I chose three guidelines that I hope will be useful and give you some insight. They happen to come from the information on auto as well as the information on moving from C++ 11, excuse me, C++ 98 to C++ 11 and 14. And as it turns out in this particular presentation, I'm not talking about anything specific to C++ 14 at all. I'm only talking about C++ 11 stuff. It just kind of worked out that way. And the first guideline I want to give you is to prefer auto to explicit type declarations. So fundamentally, you have a choice here. So if I want to create some widget W, I have a choice. I can say, okay, I want a widget called W by giving the type explicitly and then I initialize it with some expression of type widget. Or what I could do instead is I could say auto W and then give an expression of type widget. In both cases, they will create a widget object. So what I'm encouraging you to do is to not type out widget expressly but instead to use auto, which leads to the question, why am I advocating that? So we'll start with the little stuff first. The little stuff is that using auto saves you some keystrokes, which is important from a typing perspective. It is in some sense more important from a reader's perspective because the more you can get rid of noise that's in your source code, the easier it is for people to understand basically what you are trying to accomplish. So if I have, I guess this is the day for hitting wrong buttons, if I have a vector of string iterators, VSI, so it's just a vector of iterators into some strings, and I want to do something for every iterator in that container, what I could say is, okay, for every string iterator SI in the container VSI, where I'm explicitly stating the type, or I can just say, listen, for every iterator SI in the container, and I just use auto here, my feeling is in this particular case, explicitly stating that it's a string iterator does not add any information that is not already present in the source code. Anybody reading this has got to know this is a container of string iterators or they'll never understand the code. So I think auto makes it easier to understand and of course easier to type. Now, the easier to type thing normally is not a very compelling argument, but there are some cases where it can be helpful. So if I'm writing an algorithm taking a begin and an end iterator, and I decide what I want to do is I want to make a copy of the first element in the range, and we'll make the assumption it's not an empty range. Well, if I have an iterator pointing to something, the question is, what is the type of the thing that it refers to? And I know you all roll your eyes and you say, well, of course, it's type name, standard iterator traits of iterator, colon, colon, value type. Who does not know that? Actually, it turns out several people don't know that. And even if you do know it, I don't know how many people have said, you know, what I'm really looking forward today is typing this many, many times. That's not really productivity flowing through your fingers. Now, in this particular case, you can just say auto first element is star B. This is very clear. B points to the first element of the range. So this is going to be a copy of it. So I think it's clearer for people to understand. Certainly, it's easier to write. However, that's really a syntactic convenience. There are some more compelling technical reasons for using auto. There are a number of cases in C++ where we think we know what the type of something is, but we're kind of loose about it. We're not actually specific. And here's a common kind of mistake. So if I have a vector event and I call the size member function, now, it turns out that the return type of calling size on a vector, in this particular case, it's vector event, colon, colon, size type, which is a type def for some unsigned type. But the return type is supposed to be vector event, colon, colon, size type. I don't know anybody who uses that. What most people do is something like saying, ah, it's an unsigned close enough for government work. It usually works. But as a specific example of a case where it can fail, so if you are on a 32-bit window system, this is completely safe code. There is nothing wrong with this. But the only reason there is nothing wrong with the code in the sense that it will actually work under all conditions is that an unsigned and a vector event, colon, colon, size type on that platform with that compiler are both 32 bits. So they're the same thing. It doesn't matter. But if you go from 32-bit windows to 64-bit windows, suddenly things change because on a 64-bit windows platform, an unsigned is 32 bits, but a vector event, colon, colon, size type is 64 bits, which means as long as the number of elements in the container is 0, 4 billion or fewer, it will work. But if it exceeds 4 billion, you've got a problem. Now, how many people when writing their unit tests are testing with containers of more than 4 billion elements? Let me go to my spot where I can see you. I'm not seeing too many. I'm seeing no hands. This is the kind of mistake that can creep in because you're not actually using the right type. If on the other hand you said auto of size is v.size, you automatically get the right type on the right platform. So now we're moving beyond simply it's easier to type. Now we're getting into some actual technical reasons. I have a map where the keys are strings and the values are integers. And now I decide what I want to do is some operation, it's a read-only operation on every one of the elements of that map. So it would be common for people to say, all right, for every reference to a constant pair from string to int because I know that a map holds pairs, the keys of string, the values of int, it's a read-only operation I want to perform so I don't want to copy that pair. So I'm going to have it by reference to constant. So this says for every one of those pairs in the map do something with it. It seems entirely reasonable. This will compile. This will run. This will run slowly. And the reason this will run slowly is because although this is a map from string to int, you may dimly recall that the key part of a map is declared const. So the contents of a map are actually const strings and ints. That's the type of pair stored in the map. It's a pair of a const string and an int. What this code says is take every one of those pairs consisting of a const string and an int. From it, copy it into an unnamed temporary pair of type string and int. So you just pay to copy everything. Bind that temporary to the reference to const and do something on the temporary, which is probably not what you wanted to do. Now, issue number one, performance problem. You're copying everything even though you specifically pass things by reference to const to avoid copying them. But there's some more subtle issues here, too. Inside the loop here, which I'm not showing, but inside the body of the loop, if you are assuming that you actually have a reference to a pair in the map, you have a reference to an element of that map. You're assuming, for example, you could take a pointer to it and get a pointer to a pair in the map. But with the code the way it's written here, if you take a pointer to it, you will take a pointer to the temporary copy of what's in the map. And the validity of that pointer will go away at the end of the loop iteration. You will be left with a dangling pointer. So if you stored that pointer someplace, maybe in another data structure, thinking that its lifetime was valid as long as the elements of the vector, you have a really fun way to spend the dark Norwegian winters debugging your code. However, if instead you decided to use auto, because that's what the cool kids do, you could just say const auto ref, and this will automatically pick up the appropriate type that is stored in the map. It's still stored by reference to const. This won't generate any temporaries at all. This will actually give you a reference to the elements of the container. Any questions about how this works? Yeah. So the question is what type would be deduced for auto here, essentially. And the answer is auto will deduce the actual type of what is stored in the container. So it's going to be a pair of const, string, and int. That's what auto will expand into. All right? At this point, I hope you are beginning to appreciate that auto is not a matter of making things easy for people to type, although that is part of it. That's sort of the initial allure. There's more to it. There's performance issues. There's correctness issues. Auto simply helps you for making certain kinds of mistakes. Let us suppose you create a function object. Probably the easiest way to create a function object is by using a lambda expression. If you create a lambda object, create a closure technically, and you want to store it in a runtime data structure so you can use it later. Maybe for example, you have a container of callbacks. So when an event occurs, you want to call some arbitrary number of functions. You need to be able to store the lambda somewhere. Now what you could do is you can store it using a standard function object here. So this is actually an object of a template which will store the closure created by the lambda. This will work fine. However, a standard function object is essentially fixed in size. During compilation, the compiler sets aside a certain amount of space for a function, a standard function object, because it's just a class instance. It uses a certain amount of memory. So the question is, what happens if the closure that is created from this lambda, what if the closure won't fit in that chunk of memory? Because it might not. It might be too big. No problem. Standard function under those conditions will allocate heap memory and will store the copy of the closure on the heap, which leads to the observation that the closure might be stored on the heap. So using this, depending on the size of the closure created from the lambda, which is determined by the number of objects that are captured by the lambda, you might pay for heap allocation. Of course, you'll pay for heap deallocation later. If I now take the resulting object, this is F1A, so this is a function object. If I now take this function object so I can call it, and I now say FLA of 22, this is an invocation of the function object. I'm calling the function object. Now internally, the function object might hold a pointer which points off to something on the heap, which is actually ultimately what's going to get invoked. And the reason that's important is because it means that this function call here possibly is an inline function call, but possibly is an out of line function call. Maybe it's inline, maybe it's not. It depends on a combination of factors, one of which is just how good the compiler is. Code works, code runs, or if you take exactly the same lambda and instead store it in an auto object, the compiler will figure out the type corresponding to this lambda, whatever that type is, the compiler figures it out. Once the compiler knows what the type is, it knows exactly how much space is needed to store an object of that type. It will therefore take an object of that type and it will put it on the stack in an object. This will not be stored on the heap under any conditions. So this is definitely not on the heap and this is not an optimization issue. It's just not on the heap. And when I now make the function call here, I'm going to say the closure call is typically inlined. In fact, I would assume it is going to be inlined. The only reason I say typically is because there's some language lawyers in this room and I have to watch out because the standard is not 100% guaranteed that it's going to be inlined. Having said that, it's going to be inlined. Maybe not in debug mode. So we end up with a situation with using a standard function object, unknown type. We possibly incur heap allocation and deallocation. We possibly give up inlining. Typing auto instead gives us definitely no heap allocation and deallocation and also essentially guarantees us inlining. Now, in this case here, I'm showing a function object created from a lambda. There's other ways to create function objects. So if you are one of the people who likes to use bind and Nico's talk not withstanding, there actually are some people who do like to use bind. Most of them are part of a 12 step program to eventually get them away from it. But there's still a few people going through that process. And in fairness, there's a lot of legacy code that uses bind because bind is a perfectly valid tool prior to C++11. So this idea of using auto to store function objects, it's not a lambda specific thing. It's valid for any function object. So I have encouraged you to use auto instead of using explicit type declarations. It's great advice, except when it doesn't work. Because sometimes auto does the wrong thing, which means we need to have a better understanding given the technical advantage we've talked about of when auto does the wrong thing and what to do about it. So the first thing it has to do with brace initializers. If I say auto, give a variable name and then I give a braced initializer, special rule in the standard. This deduces a type of standard initializer list of int. Now, if I only show you that piece of information, you might go, well, all right, that doesn't seem too bad. The problem is if I say auto x2 gets 5, it deduces a type of int. If I say auto x3 parenthesis 5, it deduces a type of int. So if I say auto and a type like an int, some syntaxes deduce the type of int. But if you use braces, it will deduce a different type. In particular, if I say braces like this with or without the equal sign, it makes no difference, they both deduce an initializer list of int. How many people have made this mistake in their code? Oh, you guys have not lived. It is a veritable right of passage when moving to C++11 to start typing auto and then suddenly find out that nothing is working the way that you expect only to find out. It's because you thought you were creating an int here and you actually created an initializer list. It's only the case for braces. Fundamentally, if I say auto x and then I have a braced initializer, the equal sign is optional. It does not matter whether the equal sign is there. It's the braces that are important. Basically, under these conditions, the type of the variable you are creating is not going to be the type of the expression. So decal type of x, the type of the variable you're creating is not going to be equal to the decal type of expression, the type of the expression you're using to initialize it, which is a little strange because normally you say auto deduces the type of the initializing expression. This is the one case where it does not. In this syntax here where I have auto and I have braces, that's the combination that leads to trouble. Auto and a braced initializer. Again, the equal sign doesn't make a difference. When you have that combination, then the type of the variable you are creating is more or less equal to an initializer list of the type of the expression. And the reason I say more or less equal is because I actually showed you what it was equal to, it would keep going over that direction for a ways. But frankly, what it is equal to is not anywhere near as important as understanding that it doesn't deduce the type that you expect. Yeah. The curly braces actually indicates that it's supposed to be an array. So the comment is that the curly braces initializes that it's supposed to be an array. The curly braces are used to mean more than one thing is the problem. In this particular context, it would be perfectly reasonable to say it's trying to interpret it as a sequence of values. C++ does not apply that interpretation consistently. It's kind of a bigger story. Regardless of whether it is applied consistently or not, what I will say is that this is literally the only place in C++ where a braced initializer will be deduced to have this type. As an example, if you take a braced initializer and you pass it to a template, type deduction will fail. A braced initializer does not have a type. It's just a special rule for auto. So many people have tried to say, okay, this is how I'm going to think about it. And there are ways to think about it that can help reduce the confusion. But as far as I know, there is no single interpretation which is consistent across the entire language. Another problem for type deduction, for auto type deduction, is hidden proxy types. So I'm going to talk about vector bool. If you've had a proper education in C++, when you see vector bool, you begin to feel nauseous. And because you've been taught you should never use it or possibly you shirk in fear. The only reason I'm using vector bool is because it is part of the standard library, which means you can verify everything I'm going to tell you by going to your standard library implementation and looking inside the code to see exactly how it's implemented. This is a representative of a large class of libraries that use what are known as proxy types. I only use this one because you can check this one out yourself. And I'll get back to that in a moment. So if I have a vector of bool, a vector of bool is a packed representation of bools. Every boolean takes a bit. So if I now say, okay, bool b1 is vb sub 5. Now in this case here, the type of b1 is bool. Unsurprising. I've declared it to be bool, so b1's type is bool. This is not a surprise. If I say auto b2 gets vb sub 5, we have a problem. The reason we have a problem is because vb sub 5, the indexing operator for a vector, the indexing operator for a vector normally returns a reference to the indexed element. If I say vb sub 3, I normally get a reference to the third element. You can't have a reference to a bit. Small addressable thing in C++ is a char. You can't have a reference to a bit, but this is a vector of bits, basically. So the question is, how do they make that work? The way that it works is because vb sub 5 for vector of bool returns an object of type vector of bool, colon, colon reference, which is actually a class. Don't be fooled by the name. It's a class. It happens to be called reference. So when I say auto b2, the type I get back is vector of bool, colon, colon reference. So this code compiles. But you don't have the type you think you have. In many, many cases, you'll continue to use the object. It'll behave exactly the way that you think it's going to behave because there's an implicit conversion from vector of bool, colon, colon reference to bool. So if you ever try to use this thing in a boolean context, it converts and life goes on. Almost like they designed it that way. But the fact that the return type of the array bracket operator on a vector of bool is not a reference, you can tell from time to time. For example, if I were to assert that the address of b1 is not equal to the address of b2, which makes sense. Local variable v1, local variable b2, they had better not have the same address. If they had the same address, the compiler was doing something very peculiar. So I should be able to assert that they definitely are not at the same location on the stack. That assertion will not compile. And the reason the assertion won't compile is because you are attempting to compare a pointer to a bool with a pointer to a vector of bool, colon, colon reference, and you can't compare two different pointer types that have nothing in common. So the code won't compile. Think about proxies. Proxy objects are designed to stand for something else. So a vector of bool, colon, colon reference object is designed to stand for a bool. And there are some assumptions made usually by implementers regarding the lifetime of those temporaries. And in particular, a common assumption is that they're not going to live beyond the end of the statement. As an example, in GCC 4.7, probably still true in 4.9, I just haven't checked. I don't check every time a new version of the compiler comes out. In GCC 4.7, vector of bool, colon, colon reference, that type, and you can consult the library to find out what it does, that type contains a pointer to a word on the machine with the referenced bit. Remember, it stands for a bit. So what you actually get back is an object which has a pointer, the pointer points to the word, and it's got an offset which tells it which bit in the word is the one that it points to. So a vector of bool, colon, colon reference is a pointer and an index that says this word, this location in the word. That's how it's implemented on that particular library. At least it was in GCC 4.7. Now that means we have a pointer to a chunk of memory and that leads to the possibility that we can have a dangling pointer if the lifetimes aren't correct. I have an example. So here's a function called make bool veck. It's a factory function. It returns a vector of bool. So this is a function, creates a vector of bool, returns it as its return value. Now what I do is I say make bool veck, that's the function call. So make bool veck gives me this return value which is a vector of bool. I index it to get element number three which we will assume is a valid element. So now I have one of these vector of bool, colon, colon reference objects. It contains in GCC's case a pointer and an index. But I've declared the result type to be of bool. So there's an implicit conversion from a vector of bool, colon, colon reference into a bool. So what it does is it takes the pointer, goes to the word, finds the bit, sees if it's true or false, sticks the value in b1, life's wonderful. Everything works. Instead, if I say, okay, auto bit is make bool veck sub three, again call make bool veck, get a temporary vector of bool, index into get element number three which we will assume exists, get back a proxy object which we now store. So now I've got this proxy object called bit. It contains a pointer to the vector of bool that was returned by this function. That would be the vector of bool that will be automatically destroyed at the end of the statement because it's a function's return value. Which means by the time we finish executing this lovely little semicolon here, we have this charming object on the stack which contains a pointer to memory which no longer corresponds to a vector of bool. And now I just say, okay, b2 is of type bool and it's a copy of bit. This has undefined behavior. Now these two statements appear conceptually to do the same thing but they don't do the same thing. So this is an example of where auto deduces the wrong type. I mean auto deduces the right type, just the wrong type from what we want. As I said, the reason that I chose vector of bool is because you can verify everything. I'm telling you by looking at your library implementation to see exactly what they do with vector of bool, colon, colon reference. Have a field day. I'm not lying to you. But you're probably not using vector of bool. But if you are using other third party libraries, especially in areas where you're trying to get maximum performance, which turns out to not be terribly uncommon for C++ developers, you could well be using a library that is using proxy types. A while ago I took a look at the boost library. So this is an example of some boost libraries. Boost Ublast, boost expressive, boost proto, boost meta state machine. All of those libraries use proxies. How do I know? They document it. I do not know of an easy way to avoid having auto deduce a proxy type. It alas requires some knowledge of the library that you are using. Essentially what's happening is when you use auto with those kinds of libraries, it is deducing an implementation detail that the library author had hoped to keep hidden from you. And you need to be aware that if you use auto, there is a possibility that you're going to have that problem. So if you know you're using this kind of a library and the only way I know of to know that is to simply read some documentation about the library, you need to use auto with greater caution. Doesn't mean you don't use it. Just means you need to be aware that you don't want to use it probably all the time. And for what it's worth, there actually have been some proposals for C++ 17 or maybe C++ 20 or C++ 23. Maybe they'll fix it, but it doesn't do us any good right now. So for the foreseeable future, you just need to be aware of this problem. When I talk about auto, some of these people are very resistant to the idea. And they go, you know, I'm not going to be able to understand the code and I'm very concerned about having maximum code clarity. This is fine. So if you believe in your professional software engineering judgment as engineers that if the explicit type is clearer, if it will yield clearer, easier to understand, easier to maintain code, great. That's what they pay you for, is to use that engineering judgment, use it. But I do think it is important to bear in mind that first there's been a ton of success in other languages with similar features. C++ is not at the cutting edge of type inference. C++ is at the leading edge of type inference. This caused me the trailing end of type inference. So this is not new territory. And I also remark as a practical matter, a lot of people using IDEs, for example, Visual Studio, the type of an auto-divisible variable is actually visible in the IDE. So although it may not be expressly visible in the source code, it could be visible through a tool that you are using in your developing your code. So I think you should bear that in mind when making the decision whether to use auto or not use auto. So my guidelines are to prefer auto to explicit type declarations, which I do think yields, generally speaking, easier to write, easier to read code in many cases and avoid certain kinds of tricky errors involving either performance or correctness as we have seen. And certainly you should definitely remember that auto plus a brace initializer will always deduce an initializer list type. Any questions on that? I'm sorry, time's up. New guideline. I want to talk about parentheses versus braces, which builds a little bit on Nico's talk last time. My guideline, and I spent literally years trying to figure out what is my advice about parentheses versus braces. And my advice is understand the trade-offs because I don't think there is a great piece of advice for what to do. We start with the notion of uniform initialization. Uniform initialization. It's uniform. We can use it everywhere. Braces, it's great. So if we want to initialize an integer with 44, this will work. Remember, don't use auto. If I have a struct and I want to initialize the fields of the struct, I can use braces. Great. If I have a string and I want to give it a value, I can use braces. Oh, so wonderful. If I have a class, it has a data member, I can use braces on the member initialization list. This is so cool. If I have an array, I can use braces. Somebody mentioned a lyric. It's supposed to have values of an array, but that's not the only place. If I have a vector, I can use braces. If I have a raw freaking pointer, I can use braces to initialize its value. Not that anybody would use pointers. Uniform initialization. That's the sales pitch. You can use it everywhere. I don't use the term uniform initialization. And the reason I don't use the uniform initialization is because that suggests it's uniform. You should use it everywhere. And I don't believe that that is reasonable advice. I think there's a lot of advantages to what's known as uniform initialization. However, I prefer to refer to it as brace initialization. That's what I'll be calling it from now on. So, first thing about brace initialization is it has the unique feature which is that narrowing conversions are illegal. Only brace initialization will reject narrowing conversions. A narrowing conversion basically means that the compiler cannot guarantee that the value of a larger type will be able to be stored in a smaller type. So, as an example, if I have a point which has two integers and I say point P1, curly brace 1, 2.5. Now, in C++98, this was completely fine code. The 2.5, which is a double, would be truncated to initialize Y. So, in C++98, that compiles has well-defined semantics. In C++11, this code will not compile. And the reason it won't compile is because I'm using braces and this is a double and that's an int. And 2.5, last I checked, cannot be exactly represented as an integer. Code won't compile. So, if you want it to compile, you can make it compile, you just do a cast. All the cool kids use static casts. C casts. But it's important to understand this notion of rejecting narrowing conversions exists in exactly one place in the language. Only brace initialization. So, if I say int A is 2.5, this used to compile because it goes back to the days of C and it still compiles. It'll truncate to 2 and initialize your integer. No warning, no error. Well, no error. Might get a warning. But if you try to do exactly the same thing using braces, the code will be rejected. Given that narrowing conversions normally don't make any sense, like I want to store a double as an int, an implicit truncation, I think it's a feature that braces don't let you do that. That's a plus. We now have two different syntaxes for calling constructors. So I can say, widget w1 and I pass some arguments in curly braces, widget w2 and I pass arguments in parentheses. They both work, usually. Both choose the best match constructor. However, there are different rules for what it means to be the best match. Now, this is something that Nico talked about in his last presentation. Only brace initialization matches initializer list parameters and those matches are actually preferred. Let me give you an example. So this is a simplified declaration from the standard library, from the standard vector class. So here's vector. It takes a number of elements and a value. It takes an initializer list of t. This is not affected by the change that Nico was talking about at the end of his last talk. So if I say v1 with 100, 5, notice that I'm using braces. Now, because I'm using braces, I could pass the 100 is the size and the 5 is the initial value. Two arguments. That would work. Or I could say 105 are two initial values to go into the vector. There is, in some sense, ambiguity here. But the language rules say when you use braces and it is possible to call an initializer list constructor, that is the one that you call. It's not ambiguous. So this means that the size of the vector is 2 and the values are 105. It calls this second constructor. If I take exactly the same code and I say, you know, I'm a traditionalist, I like the old ways. And the old ways of passing constructor arguments was to use parentheses. So exactly the same code. But now I use parentheses. Under these conditions, now it calls the first constructor because initializer list constructors are only considered if you use braces. If you don't use braces, they're not even considered. So the result of this is a vector whose size is 100 and values is set to 5. All of them are 5. So it does something different. They're not interchangeable. Now, this only makes a difference if you actually have an initializer list constructor. So here's a gadget class. It has one constructor. It takes two integers. So if I say I want to create a gadget G1 with 10 and 20 or a G2 also with 10 and 20 braces and parentheses, they do exactly the same thing. They both call this constructor. Makes no difference. They do the same thing. If I now say G3 with 89.5 and 0, notice that I'm using parentheses, compiler says, okay, 89.5, that's a double. I'm going to pass it to an int. No problem. I'll take that 89.5, get rid of that pesky 0.5. We didn't want that half of a number anyway. Compiles. But if I do the same thing inside curly braces, it won't compile. And the reason it won't compile is because this double can't be expressed in that integer exactly. Again, you get brace initialization, rejects narrowing conversions. Yes? Is the question, does this also apply to larger integer types going to smaller integer types? Correct. It does. Essentially, the way it is is the only time that if you have a larger type going to a smaller type, the only time that that will be permitted to compile is if the compiler can prove that it will fit as an example. If I said I want to initialize an int and I said the initial value is going to be 22 long, so long is bigger than an int in general. But we know that the value 22 will fit in an int. That will compile. On the other hand, if I said long L equals 22 and then initialize the int with the long in curly braces, that won't compile. Make sense? Yes. Okay. So basically, it doesn't compile unless the compiler can guarantee it's okay. Was there a question? What happened to the specter, your example showing us that there is no specter in the case of the initializer list. Okay. So all right. Is your question, what would happen here if we did not have the initializer list constructor? Okay. If we didn't have the initializer list constructor, they'd both call the first constructor. They do exactly the same thing. That's essentially the same as this example here. All right. So now, this leads to some really interesting situations. So I got tired of using widgets. I've been talking about widgets for 20 years. I'm moving on. I'm progressing. I'm growing. Let's talk about thingies. So I got a thingie, takes an int and an int, and thingies take initializer list of double. So if I say parentheses with 10 and 20, it calls the first one. Remember, with parentheses, it will never call an initializer list constructor. It's very simple. It won't call it. If I use braces here, notice that these are integers, and x and y are integers, and this is an initializer list of double. It's going to be a better match to go from two ints converted to doubles than two ints calling x and y. If there is a way to make the initializer list constructor work with braces, that's what it'll get called. If I have T3, 89.5 and 0, this is a matter of type conversion. So this will call number one. Notice that I've got a double here. This is a double. It still won't try to be converted into an initializer list. If you are using parentheses, the compiler literally pretends as if that constructor, the one with the initializer list, did not exist. All right. Now, this means that we can get some really interesting implications if we add new constructors to classes. Now, I want to be clear here. Anytime you have a class with some interface and you add a new overload to that class, it is possible that existing code that used to call some function in your class may now start calling the other overload. That's inherent when you add a new overload. So there's nothing special about initializer lists here. If you add a new overload to an overload set, code that used to compile and call one overload might now call the new overload, which is probably why you added it. So in this case here, I've got a widget. I'm using braces because I've embraced the uniform initialization lifestyle. This calls widget with 10 and 20. Life goes on. Life's good. Somebody comes along later. They add an initializer list constructor to it. Notice that it's an initializer list of float. This code, unchanged, legacy code, no longer calls this constructor. It now gets converted to two floats because braces really want to match an initializer list constructor. If there is any way to make it compile, it will compile. This is neither a good thing nor a bad thing. It's just a characteristic of the way overload resolution works in C++ and you need to be aware of it. Now, presumably when you added this initializer list overload, you said anytime anybody is using braces, I want to treat that as an initializer list. That's not what you meant. You need to have a little talk with the API designers. So here's our situation. Essentially, we've talked about this here. So the guideline is to distinguish between parentheses and braces when initializing objects. Any questions about that? All right. The last guideline I want to talk about then is to make const member functions thread safe. We have known from the beginning of C++ that there were two different ways to interpret const nest. There's logical const nest, which means conceptually nothing changes. And there's bitwise const nest, which means none of the bits inside an object actually gets modified. So bitwise const nest, this is what compilers enforce, conceptual const nest, this is what developers should implement. With any luck, this is completely old news. We've been talking about this since 1995. I've been talking about this since 1995. My God, what happened to me? 1995, never mind. All right. Mutable. Also available since 1998. Mutable, as I like to put it, tells the compiler, ha, ha, I was kidding about the const. So a mutable data member is permitted to be modified even inside a const member function. It is very useful when implementing conceptual const nest in particular for doing things like caching, which is probably its most common use. So here I have a widget class. It has a member function called magic value, which computes a magic value. So computing the magic value conceptually does not change the widget. If I get to a widget and say, so what's your magic value, it goes crunch, crunch, crunch, crunch. Here's my magic value, but it didn't change the widget. It's the same widget it was before. If, however, we assume that it's computationally expensive to determine what the magic value is, we would prefer not to have to compute it unless somebody asks for it, which means we don't want to compute it up front. But if somebody does ask us for it, we don't want to have to compute it multiple times because it's expensive, which means we'd like to cache it if we ever do compute it. So we can implement it like this. We could say, all right, I'm going to create a mutable bool called cache valid and a mutable int cached value. What I'm going to do is if I ever compute the value of the magic number, I will store it and then I will say that the cached value is valid. So I can look it up again in the future, save myself some time. So here's my code. If the cache is valid, great, I just return the cached value. Now, if the cache is not valid, maybe because nobody's ever called this function before, then what I do is I say, okay, the cached value is, I call some expensive computation, I say the cached valid is now true and I return the cached value. Perfectly standard way of implementing a cache using mutable. In C++ 98, there's no C++ 11 here. Works fine. Life's good. C++ introduced this tiny new feature. It's called concurrency. It has some minor implications for programmers. For example, nothing works anymore. A few things work. But as an example, let us suppose that I've got some widget W and now in thread one, I say auto v1 is W magic value. Crunch, crunch, crunch, crunch. It's figuring out the magic value. Thread two at the same time calls W magic value. Crunch, crunch, crunch, crunch. Just doing the same thing. Now, I want to go back a slide and show you that magic value I have declared to be a const member function. It does not change the conceptual state of the widget. You should be able to have multiple people reading a widget simultaneously. They're all readers. And because I declared it const, because it is logically const, I have declared these two data members mutable. The problem I have is that in this case here, here's thread number one, it's calling a const member function. It's a read operation. Here's thread number two. It's calling a const member function. It's a read operation. If I know that I'm writing code with multiple threads simultaneously reading something, that does not require synchronization. I don't need to get a mutex or anything else if all my threads are readers. It's safe to read things simultaneously. It's conceptually safe. The problem is that magic value, in case you've forgotten, that was the previous slide, those look like write operations to me. The const member function is conceptually const. It fulfills the conceptual const requirement. The problem is it's not bitwise const. And that makes a big difference when it comes to concurrency. So this means both of the reads may actually write the mutable data members, which means we have two threads which are both readers and writers. That's the definition of a data race. That means undefined behavior. And that's just bad. What's key here is there's nothing wrong with the calling code. Both threads are performing read operations. There's nothing wrong with those threads. They're doing perfectly reasonable behavior. Possibly the code was written so they would do a lot of reads. So you can't blame them. Well, if the client code's not wrong and the behavior's undefined, that pretty much means the implementation code is wrong. No other way to look at that. Widget magic value is a broken function. Now, fixing it is really easy, and this is where C++11 comes in. So all we have to do is, okay, I'm going to slap in a mutex, and I'm going to declare the mutex to be mutable because locking a mutex changes its state. It's a non-const operation. Unlocking a mutex also changes its state. Non-const operation. So I need to perform non-const operations on the mutable inside the const member function. Sorry, on the mutex. So no problem. I lock the mutex at the beginning of the function. I perform the operation to figure out what the value is, and then this object in its destructor will automatically unlock the mutex, so this closed curly brace here unlocks the mutex. This works fine. This fixes the problem. Now, in some cases you don't need a mutex. In some cases you might only need an atomic variable, which is a variable where operations on it are guaranteed to be viewed atomically by other threads. For example, let's suppose I'm keeping track of how many times the function is called. So here's my gadget class. Here's a get-wait function, and for whatever reason I want to know how often is get-wait called. Under those conditions I could have an int and I could have a mutex to limit access to the int, but or what I can do, which is a little faster in many cases, is I can just use an atomic unsigned. So what that means is that when I call plus plus call count, that's a read modify write operation. It's guaranteed to be executed atomically, typically through a single underlying machine instruction. Typically it is faster at runtime than using a mutex. So this works fine. So notice I'm not saying you should always use a mutex. This doesn't need a mutex. However, if we go back to our example with the cache and we say, okay, I'm going to make the cached value an atomic int and the cached valid an atomic bool, this is the way of thinking it says, well, if atomic is cheaper than using a mutex, I'm going to use them all over the place. I'm a C++ programmer. I don't pay for what I don't need. So here's the code again. Now in this case I've rewritten it a little bit differently here. So I check the cache to see if it's valid. If so, I return cache value. Otherwise, in this case, I perform an expensive computation number one, which I store as V1. I now perform an expensive computation number two, which I store as V2. I now say, okay, the cache is valid. That's true now. And I return the cache value, which is V1 plus V2. So I've just broken the expensive computation into two pieces. It's all I've done. This is not thread safe. Why not? Let me get in my spot. Okay. Now I can see you. Yes. Each of the lines are atomic, but the whole transaction is not atomic. Okay. You are correct. Each of the lines is atomic, but the transaction is a whole, excuse me, the whole thing is not transactional. The problem is that one thread can come in, see that cache is not valid, start doing the expensive computations, and then can say cache valid is set to true. In the meantime, another thread comes in and goes, oh, the cache is valid because the other thread just set it to true. So it now returns the cache value, but we haven't computed the cache value yet because the first thread hasn't yet performed the sum operation. Oops, wrong answer. Only happens once every five years or so. So we don't really care. We can fix that operation, you say. No problem. What we'll do is we'll compute the cache value before we set cache valid to true. Right? That'll fix the problem. It will fix the problem. You'll get the right answer. This works. It just works harder than it should. Thread comes in, checks to see if cache valid is true. It's not. Starts performing the expensive computations and then adds V1 and V2 together. At this point, we've performed the expensive computations. We are ready to go. Another thread comes in, checks to see if cache is valid. It's not yet because we haven't yet executed this statement in the first thread. Enters the loop, starts those expensive computations all over again. In the meantime, 22 other threads also check cache valid simultaneously because you are running on a 48 core machine. And they are now all performing the expensive computations. You are really going to get the answer now. Yes? So the question is, won't each thread get its own value? If we proceed on the assumption that there is only one magic value for the widget, in other words, no matter how many times you compute it, it will always get the right answer, every thread should ultimately get the same answer. It's just that we're going to compute those sub-computations every time. Let me think for a minute. Let me put it this way. That's the best case scenario. Okay? Let me check and see. Okay, in this particular case, because they're both atomic, I don't have to worry about partial results being read by thread, so I believe that what I said is true. But even if this does return the right answer all the time, it's much more expensive than it needs to be. So, you know, the problem is if you have more than one data member and they need to be kept in sync somehow, you need a transaction. If you need a transaction, almost certainly what you want to do is use a mutex. So declare the mutex as I showed you before. Now cache value and cache valid, these don't need to be atomic any longer. Now atomics are more expensive than non-atomics, cheaper than mutexes, but more expensive than non-atomic. So these can now become regular, ordinary, instant bools because we're now using the lock guard solution. So if you have a const member function in C++11, the first version of the language that supports concurrency, it should either be bitwise const or it should be internally synchronized, meaning that if it's doing read and write operations, outside callers can call it safely without having to acquire a mutex. If you find yourself in a situation where you know for a fact that your class will never be used concurrently, you just know that. Simplest way to know it, you're writing a single threaded application. There's no other threads. Under those conditions, then using atomics or acquiring and releasing a mutex is incurring a cost you do not need. If the logic of your program is such that we know that there will never be for a particular kind of object two threads using it simultaneously. For example, if I have a phased design and in the first phase of the design, only one thread uses the object and in the second phase, only some other thread uses the object, but they never access the object simultaneously. If you know that, if by design of your program, you guarantee that they will never be simultaneously accessed under those conditions, that's an exception to the rule. So if you are writing purely single threaded applications, you don't consider think at all about threading, you can forget about this guideline, program in C++98. But if you are programming a context where it is possible for your classes to be used in a multi-threaded environment where there could be simultaneous readers, then you need to follow the guideline that I'm giving you, which is to make constant member functions thread safe. Any questions about that? So the question is how do you ensure that if you are using a third party library that they have basically done this. If you find a third party library that's not doing this correctly, you need to file a bug report with the library. I mean, it's just, it's a concurrency bug in the library. So you may have to pay the cost of the library. So you may have to pay the cost of discovering that as a bug. I mean, essentially in moving, in going from a world with only one thread to a world with multiple threads, it is not going to be surprising if third party libraries are going to have some concurrency errors in them. Yeah. In your previous example, the mutex, you could use the mutex inside the else clause. So the question is, can't I move the mutex inside the else clause? And the answer is no. Because otherwise, then I'd be reading cache valid outside the mutex. Okay. So essentially the question is, all right, I want to move the lock guard inside the else and then I'm going to make cache valid atomic so that my threads, trust me, you're still going to get in trouble. In particular on relaxed memory architectures. This leads us right down the road into memory barriers and stuff like that. And that is a hell I do not want to go to. So I'm afraid I'm going to have to cut you off if you're asking more questions about that. I have a laser. I'm not afraid to use it. Okay. So here's the summary of what I want to talk about. We talked about three guidelines, but I slipped an extra one in there. So prefer auto to explicit type declarations. It's not just about programming style. It's also about efficiency and correctness. Remember that auto plus a brace initializer will deduce an initializer list. Distinguish parentheses and braces when creating objects. I'd love to be able to say always use one or always use the other. It's not that simple. I'm sorry. Make const member functions thread safe. If you're looking for more information about this, there's a true work of literary genius in the works, I hope. I'm working on a book called Effective Modern C++. I hope it's going to be out in October. If you want other guidelines based on information that will probably be in the book, I gave a presentation last year called an effective C++ 1114 sampler. It covers different guidelines than what I talked about here. Herb setter has written a thing called elements of modern C++ style. So you can get some guidance from.
 Well, good morning. I'm Scott Myers. I thought I'd be talking a little bit about C++ today.  I am standing in the only location I can find on the entire stage where I could actually  see you. This is relevant because if you want to ask questions and you raise your hand,  I'm probably not going to be able to see you. So this is what we're going to do. Normally,  what I tell people is if they don't understand something or if they're a little concerned,  they should ask a question, which I want to encourage. If you feel uncomfortable asking  a question, what I normally encourage people to do is to look really confused and to stare  directly at me. But that's not going to work because I have lights in my eyes I won't be  able to see you. So you have two choices. One of them is you can make lots of motion  like this because I think like insects, I can in fact detect motion. So that's one possibility.  Or if you see me, go to the only location on the stage where I can see you, that might  help. Or you can start making really uncomfortable sounds as if there's just something terribly  wrong and then I'll try and figure out what's going on. So that's the plan for questions  and answers. Also, if you don't want me to see you, you need to encourage me to go any  place except that one location on the stage. I want to talk today about effective modern  C++, which is the latest project I've been working on to try to put together guidelines  for how to make the most effective use of the language. And in this particular case,  when I talk about the language, I'm talking about the new features in C++ 11 and C++ 14.  So for purposes of the discussion today, I'm assuming you already know everything in C++  98. That's old stuff. So the question is, how do you make effective use of the newer  stuff? And the outline that I have for the work that I've been doing is to break down  the general topic areas in C++ 11, C++ 14. This is what I've broken them down into. When  I started the project, I did not plan to talk about type deduction at all. I don't like  talking about type deduction. It's kind of a dull topic. I found out I was unable to  discuss C++ 11 and especially C++ 14 without explaining how type deduction works because  type deduction is widespread in C++ 11. It is essentially much more widespread in C++  14. That's a whole separate talk. In fact, I'll be giving a talk on that topic this afternoon  at 4.20. So that's a different talk. The other topic areas that I have been investigating,  putting together guidelines is auto, gets its own chapter, a whole bunch of information  on changing the habits you have from C++ 98 to moving to C++ 11 and C++ 14, different  ways of doing things. Chapter on smart pointers, chapter on R value references, chapter on  lambdas, chapter on the concurrency API. The only things I'm going to be talking about  today because we have a limited amount of time is I chose three guidelines that I hope  will be useful and give you some insight. They happen to come from the information on  auto as well as the information on moving from C++ 11, excuse me, C++ 98 to C++ 11  and 14. And as it turns out in this particular presentation, I'm not talking about anything  specific to C++ 14 at all. I'm only talking about C++ 11 stuff. It just kind of worked  out that way. And the first guideline I want to give you is to prefer auto to explicit  type declarations. So fundamentally, you have a choice here. So if I want to create  some widget W, I have a choice. I can say, okay, I want a widget called W by giving the  type explicitly and then I initialize it with some expression of type widget. Or what I  could do instead is I could say auto W and then give an expression of type widget. In  both cases, they will create a widget object. So what I'm encouraging you to do is to not  type out widget expressly but instead to use auto, which leads to the question, why am I  advocating that? So we'll start with the little stuff first. The little stuff is that using  auto saves you some keystrokes, which is important from a typing perspective. It is in some sense  more important from a reader's perspective because the more you can get rid of noise  that's in your source code, the easier it is for people to understand basically what  you are trying to accomplish. So if I have, I guess this is the day for hitting wrong  buttons, if I have a vector of string iterators, VSI, so it's just a vector of iterators into  some strings, and I want to do something for every iterator in that container, what I could  say is, okay, for every string iterator SI in the container VSI, where I'm explicitly  stating the type, or I can just say, listen, for every iterator SI in the container, and  I just use auto here, my feeling is in this particular case, explicitly stating that it's  a string iterator does not add any information that is not already present in the source  code. Anybody reading this has got to know this is a container of string iterators or  they'll never understand the code. So I think auto makes it easier to understand and of  course easier to type. Now, the easier to type thing normally is not a very compelling  argument, but there are some cases where it can be helpful. So if I'm writing an algorithm  taking a begin and an end iterator, and I decide what I want to do is I want to make  a copy of the first element in the range, and we'll make the assumption it's not an empty  range. Well, if I have an iterator pointing to something, the question is, what is the  type of the thing that it refers to? And I know you all roll your eyes and you say, well,  of course, it's type name, standard iterator traits of iterator, colon, colon, value type.  Who does not know that? Actually, it turns out several people don't know that. And even  if you do know it, I don't know how many people have said, you know, what I'm really looking  forward today is typing this many, many times. That's not really productivity flowing through  your fingers. Now, in this particular case, you can just say auto first element is star  B. This is very clear. B points to the first element of the range. So this is going to  be a copy of it. So I think it's clearer for people to understand. Certainly, it's easier  to write. However, that's really a syntactic convenience. There are some more compelling  technical reasons for using auto. There are a number of cases in C++ where we think we  know what the type of something is, but we're kind of loose about it. We're not actually  specific. And here's a common kind of mistake. So if I have a vector event and I call the  size member function, now, it turns out that the return type of calling size on a vector,  in this particular case, it's vector event, colon, colon, size type, which is a type def  for some unsigned type. But the return type is supposed to be vector event, colon, colon,  size type. I don't know anybody who uses that. What most people do is something like saying,  ah, it's an unsigned close enough for government work. It usually works. But as a specific  example of a case where it can fail, so if you are on a 32-bit window system, this is  completely safe code. There is nothing wrong with this. But the only reason there is nothing  wrong with the code in the sense that it will actually work under all conditions is that  an unsigned and a vector event, colon, colon, size type on that platform with that compiler  are both 32 bits. So they're the same thing. It doesn't matter. But if you go from 32-bit  windows to 64-bit windows, suddenly things change because on a 64-bit windows platform,  an unsigned is 32 bits, but a vector event, colon, colon, size type is 64 bits, which means  as long as the number of elements in the container is 0, 4 billion or fewer, it will work. But  if it exceeds 4 billion, you've got a problem. Now, how many people when writing their unit  tests are testing with containers of more than 4 billion elements? Let me go to my spot  where I can see you. I'm not seeing too many. I'm seeing no hands. This is the kind of  mistake that can creep in because you're not actually using the right type. If on the other  hand you said auto of size is v.size, you automatically get the right type on the right  platform. So now we're moving beyond simply it's easier to type. Now we're getting into  some actual technical reasons. I have a map where the keys are strings and the values  are integers. And now I decide what I want to do is some operation, it's a read-only  operation on every one of the elements of that map. So it would be common for people  to say, all right, for every reference to a constant pair from string to int because  I know that a map holds pairs, the keys of string, the values of int, it's a read-only  operation I want to perform so I don't want to copy that pair. So I'm going to have it  by reference to constant. So this says for every one of those pairs in the map do something  with it. It seems entirely reasonable. This will compile. This will run. This will run  slowly. And the reason this will run slowly is because although this is a map from string  to int, you may dimly recall that the key part of a map is declared const. So the contents  of a map are actually const strings and ints. That's the type of pair stored in the map.  It's a pair of a const string and an int. What this code says is take every one of those  pairs consisting of a const string and an int. From it, copy it into an unnamed temporary  pair of type string and int. So you just pay to copy everything. Bind that temporary to  the reference to const and do something on the temporary, which is probably not what  you wanted to do. Now, issue number one, performance problem.  You're copying everything even though you specifically pass things by reference to const  to avoid copying them. But there's some more subtle issues here, too. Inside the loop here,  which I'm not showing, but inside the body of the loop, if you are assuming that you  actually have a reference to a pair in the map, you have a reference to an element of  that map. You're assuming, for example, you could take a pointer to it and get a pointer  to a pair in the map. But with the code the way it's written here, if you take a pointer  to it, you will take a pointer to the temporary copy of what's in the map. And the validity  of that pointer will go away at the end of the loop iteration. You will be left with a  dangling pointer. So if you stored that pointer someplace, maybe in another data structure,  thinking that its lifetime was valid as long as the elements of the vector, you have a  really fun way to spend the dark Norwegian winters debugging your code. However, if instead  you decided to use auto, because that's what the cool kids do, you could just say const  auto ref, and this will automatically pick up the appropriate type that is stored in  the map. It's still stored by reference to const. This won't generate any temporaries  at all. This will actually give you a reference to the elements of the container. Any questions  about how this works? Yeah. So the question is what type would be  deduced for auto here, essentially. And the answer is auto will deduce the actual type  of what is stored in the container. So it's going to be a pair of const, string, and int.  That's what auto will expand into. All right? At this point, I hope you are beginning to  appreciate that auto is not a matter of making things easy for people to type, although that  is part of it. That's sort of the initial allure. There's more to it. There's performance  issues. There's correctness issues. Auto simply helps you for making certain kinds of mistakes.  Let us suppose you create a function object. Probably the easiest way to create a function  object is by using a lambda expression. If you create a lambda object, create a closure  technically, and you want to store it in a runtime data structure so you can use it later.  Maybe for example, you have a container of callbacks. So when an event occurs, you want  to call some arbitrary number of functions. You need to be able to store the lambda somewhere.  Now what you could do is you can store it using a standard function object here. So this  is actually an object of a template which will store the closure created by the lambda.  This will work fine. However, a standard function object is essentially fixed in size. During  compilation, the compiler sets aside a certain amount of space for a function, a standard  function object, because it's just a class instance. It uses a certain amount of memory.  So the question is, what happens if the closure that is created from this lambda, what if  the closure won't fit in that chunk of memory? Because it might not. It might be too big.  No problem. Standard function under those conditions will allocate heap memory and will  store the copy of the closure on the heap, which leads to the observation that the closure  might be stored on the heap. So using this, depending on the size of the closure created  from the lambda, which is determined by the number of objects that are captured by the  lambda, you might pay for heap allocation. Of course, you'll pay for heap deallocation  later. If I now take the resulting object, this is F1A, so this is a function object.  If I now take this function object so I can call it, and I now say FLA of 22, this is  an invocation of the function object. I'm calling the function object. Now internally,  the function object might hold a pointer which points off to something on the heap, which  is actually ultimately what's going to get invoked. And the reason that's important is  because it means that this function call here possibly is an inline function call, but possibly  is an out of line function call. Maybe it's inline, maybe it's not. It depends on a combination  of factors, one of which is just how good the compiler is. Code works, code runs, or  if you take exactly the same lambda and instead store it in an auto object, the compiler will  figure out the type corresponding to this lambda, whatever that type is, the compiler  figures it out. Once the compiler knows what the type is, it knows exactly how much space  is needed to store an object of that type. It will therefore take an object of that type  and it will put it on the stack in an object. This will not be stored on the heap under  any conditions. So this is definitely not on the heap and this is not an optimization  issue. It's just not on the heap. And when I now make the function call here, I'm going  to say the closure call is typically inlined. In fact, I would assume it is going to be  inlined. The only reason I say typically is because there's some language lawyers in  this room and I have to watch out because the standard is not 100% guaranteed that it's  going to be inlined. Having said that, it's going to be inlined. Maybe not in debug mode.  So we end up with a situation with using a standard function object, unknown type. We  possibly incur heap allocation and deallocation. We possibly give up inlining. Typing auto  instead gives us definitely no heap allocation and deallocation and also essentially guarantees  us inlining. Now, in this case here, I'm showing a function object created from a lambda. There's  other ways to create function objects. So if you are one of the people who likes to use  bind and Nico's talk not withstanding, there actually are some people who do like to use  bind. Most of them are part of a 12 step program to eventually get them away from it. But there's  still a few people going through that process. And in fairness, there's a lot of legacy code  that uses bind because bind is a perfectly valid tool prior to C++11. So this idea of  using auto to store function objects, it's not a lambda specific thing. It's valid for  any function object. So I have encouraged you to use auto instead of using explicit type  declarations. It's great advice, except when it doesn't work. Because sometimes auto does  the wrong thing, which means we need to have a better understanding given the technical  advantage we've talked about of when auto does the wrong thing and what to do about it.  So the first thing it has to do with brace initializers. If I say auto, give a variable  name and then I give a braced initializer, special rule in the standard. This deduces  a type of standard initializer list of int. Now, if I only show you that piece of information,  you might go, well, all right, that doesn't seem too bad. The problem is if I say auto  x2 gets 5, it deduces a type of int. If I say auto x3 parenthesis 5, it deduces a type  of int. So if I say auto and a type like an int, some syntaxes deduce the type of int.  But if you use braces, it will deduce a different type. In particular, if I say braces like  this with or without the equal sign, it makes no difference, they both deduce an initializer  list of int. How many people have made this mistake in their code? Oh, you guys have not  lived. It is a veritable right of passage when moving to C++11 to start typing auto  and then suddenly find out that nothing is working the way that you expect only to find  out. It's because you thought you were creating an int here and you actually created an initializer  list. It's only the case for braces. Fundamentally, if I say auto x and then I have a braced initializer,  the equal sign is optional. It does not matter whether the equal sign is there. It's the  braces that are important. Basically, under these conditions, the type of the variable  you are creating is not going to be the type of the expression. So decal type of x, the  type of the variable you're creating is not going to be equal to the decal type of expression,  the type of the expression you're using to initialize it, which is a little strange because  normally you say auto deduces the type of the initializing expression. This is the one case  where it does not. In this syntax here where I have auto and I have braces, that's the  combination that leads to trouble. Auto and a braced initializer. Again, the equal sign  doesn't make a difference. When you have that combination, then the type of the variable  you are creating is more or less equal to an initializer list of the type of the expression.  And the reason I say more or less equal is because I actually showed you what it was  equal to, it would keep going over that direction for a ways. But frankly, what it is equal  to is not anywhere near as important as understanding that it doesn't deduce the type that you  expect. Yeah.  The curly braces actually indicates that it's supposed to be an array.  So the comment is that the curly braces initializes that it's supposed to be an array. The curly  braces are used to mean more than one thing is the problem. In this particular context,  it would be perfectly reasonable to say it's trying to interpret it as a sequence of values.  C++ does not apply that interpretation consistently. It's kind of a bigger story. Regardless of  whether it is applied consistently or not, what I will say is that this is literally  the only place in C++ where a braced initializer will be deduced to have this type. As an example,  if you take a braced initializer and you pass it to a template, type deduction will fail.  A braced initializer does not have a type. It's just a special rule for auto. So many  people have tried to say, okay, this is how I'm going to think about it. And there are  ways to think about it that can help reduce the confusion. But as far as I know, there  is no single interpretation which is consistent across the entire language.  Another problem for type deduction, for auto type deduction, is hidden proxy types. So  I'm going to talk about vector bool. If you've had a proper education in C++, when you see  vector bool, you begin to feel nauseous. And because you've been taught you should never  use it or possibly you shirk in fear. The only reason I'm using vector bool is because  it is part of the standard library, which means you can verify everything I'm going  to tell you by going to your standard library implementation and looking inside the code  to see exactly how it's implemented. This is a representative of a large class of libraries  that use what are known as proxy types. I only use this one because you can check this  one out yourself. And I'll get back to that in a moment.  So if I have a vector of bool, a vector of bool is a packed representation of bools.  Every boolean takes a bit. So if I now say, okay, bool b1 is vb sub 5. Now in this case  here, the type of b1 is bool. Unsurprising. I've declared it to be bool, so b1's type  is bool. This is not a surprise. If I say auto b2 gets vb sub 5, we have a problem. The  reason we have a problem is because vb sub 5, the indexing operator for a vector, the  indexing operator for a vector normally returns a reference to the indexed element. If I say  vb sub 3, I normally get a reference to the third element. You can't have a reference  to a bit. Small addressable thing in C++ is a char. You can't have a reference to a bit,  but this is a vector of bits, basically. So the question is, how do they make that work?  The way that it works is because vb sub 5 for vector of bool returns an object of type  vector of bool, colon, colon reference, which is actually a class. Don't be fooled by the  name. It's a class. It happens to be called reference. So when I say auto b2, the type  I get back is vector of bool, colon, colon reference. So this code compiles. But you  don't have the type you think you have. In many, many cases, you'll continue to use the  object. It'll behave exactly the way that you think it's going to behave because there's  an implicit conversion from vector of bool, colon, colon reference to bool. So if you ever  try to use this thing in a boolean context, it converts and life goes on. Almost like  they designed it that way. But the fact that the return type of the array bracket operator  on a vector of bool is not a reference, you can tell from time to time. For example, if  I were to assert that the address of b1 is not equal to the address of b2, which makes  sense. Local variable v1, local variable b2, they had better not have the same address.  If they had the same address, the compiler was doing something very peculiar. So I should  be able to assert that they definitely are not at the same location on the stack. That  assertion will not compile. And the reason the assertion won't compile is because you  are attempting to compare a pointer to a bool with a pointer to a vector of bool, colon,  colon reference, and you can't compare two different pointer types that have nothing  in common. So the code won't compile. Think about proxies. Proxy objects are designed to  stand for something else. So a vector of bool, colon, colon reference object is designed  to stand for a bool. And there are some assumptions made usually by implementers regarding the  lifetime of those temporaries. And in particular, a common assumption is that they're not going  to live beyond the end of the statement. As an example, in GCC 4.7, probably still true  in 4.9, I just haven't checked. I don't check every time a new version of the compiler comes  out. In GCC 4.7, vector of bool, colon, colon reference, that type, and you can consult  the library to find out what it does, that type contains a pointer to a word on the machine  with the referenced bit. Remember, it stands for a bit. So what you actually get back is  an object which has a pointer, the pointer points to the word, and it's got an offset  which tells it which bit in the word is the one that it points to. So a vector of bool,  colon, colon reference is a pointer and an index that says this word, this location  in the word. That's how it's implemented on that particular library. At least it was  in GCC 4.7. Now that means we have a pointer to a chunk of memory and that leads to the  possibility that we can have a dangling pointer if the lifetimes aren't correct. I have an  example. So here's a function called make bool veck. It's a factory function. It returns  a vector of bool. So this is a function, creates a vector of bool, returns it as its  return value. Now what I do is I say make bool veck, that's the function call. So make  bool veck gives me this return value which is a vector of bool. I index it to get element  number three which we will assume is a valid element. So now I have one of these vector  of bool, colon, colon reference objects. It contains in GCC's case a pointer and an index.  But I've declared the result type to be of bool. So there's an implicit conversion from  a vector of bool, colon, colon reference into a bool. So what it does is it takes the pointer,  goes to the word, finds the bit, sees if it's true or false, sticks the value in b1, life's  wonderful. Everything works. Instead, if I say, okay, auto bit is make bool veck sub  three, again call make bool veck, get a temporary vector of bool, index into get element number  three which we will assume exists, get back a proxy object which we now store. So now  I've got this proxy object called bit. It contains a pointer to the vector of bool that  was returned by this function. That would be the vector of bool that will be automatically  destroyed at the end of the statement because it's a function's return value. Which means  by the time we finish executing this lovely little semicolon here, we have this charming  object on the stack which contains a pointer to memory which no longer corresponds to a  vector of bool. And now I just say, okay, b2 is of type bool and it's a copy of bit.  This has undefined behavior. Now these two statements appear conceptually to do the same  thing but they don't do the same thing. So this is an example of where auto deduces the  wrong type. I mean auto deduces the right type, just the wrong type from what we want.  As I said, the reason that I chose vector of bool is because you can verify everything.  I'm telling you by looking at your library implementation to see exactly what they do  with vector of bool, colon, colon reference. Have a field day. I'm not lying to you. But  you're probably not using vector of bool. But if you are using other third party libraries,  especially in areas where you're trying to get maximum performance, which turns out to  not be terribly uncommon for C++ developers, you could well be using a library that is  using proxy types. A while ago I took a look at the boost library. So this is an example  of some boost libraries. Boost Ublast, boost expressive, boost proto, boost meta state  machine. All of those libraries use proxies. How do I know? They document it. I do not  know of an easy way to avoid having auto deduce a proxy type. It alas requires some knowledge  of the library that you are using. Essentially what's happening is when you use auto with  those kinds of libraries, it is deducing an implementation detail that the library author  had hoped to keep hidden from you. And you need to be aware that if you use auto, there  is a possibility that you're going to have that problem. So if you know you're using  this kind of a library and the only way I know of to know that is to simply read some  documentation about the library, you need to use auto with greater caution. Doesn't  mean you don't use it. Just means you need to be aware that you don't want to use it  probably all the time. And for what it's worth, there actually have been some proposals for  C++ 17 or maybe C++ 20 or C++ 23. Maybe they'll fix it, but it doesn't do us any good right  now. So for the foreseeable future, you just need to be aware of this problem. When I talk  about auto, some of these people are very resistant to the idea. And they go, you know,  I'm not going to be able to understand the code and I'm very concerned about having maximum  code clarity. This is fine. So if you believe in your professional software engineering  judgment as engineers that if the explicit type is clearer, if it will yield clearer,  easier to understand, easier to maintain code, great. That's what they pay you for, is to  use that engineering judgment, use it. But I do think it is important to bear in mind  that first there's been a ton of success in other languages with similar features. C++  is not at the cutting edge of type inference. C++ is at the leading edge of type inference.  This caused me the trailing end of type inference. So this is not new territory. And I also remark  as a practical matter, a lot of people using IDEs, for example, Visual Studio, the type  of an auto-divisible variable is actually visible in the IDE. So although it may not  be expressly visible in the source code, it could be visible through a tool that you are  using in your developing your code. So I think you should bear that in mind when making the  decision whether to use auto or not use auto. So my guidelines are to prefer auto to explicit  type declarations, which I do think yields, generally speaking, easier to write, easier  to read code in many cases and avoid certain kinds of tricky errors involving either performance  or correctness as we have seen. And certainly you should definitely remember that auto plus  a brace initializer will always deduce an initializer list type. Any questions on that?  I'm sorry, time's up. New guideline. I want to talk about parentheses versus braces, which  builds a little bit on Nico's talk last time. My guideline, and I spent literally years  trying to figure out what is my advice about parentheses versus braces. And my advice is  understand the trade-offs because I don't think there is a great piece of advice for  what to do. We start with the notion of uniform initialization. Uniform initialization. It's  uniform. We can use it everywhere. Braces, it's great. So if we want to initialize an  integer with 44, this will work. Remember, don't use auto. If I have a struct and I want  to initialize the fields of the struct, I can use braces. Great. If I have a string  and I want to give it a value, I can use braces. Oh, so wonderful. If I have a class, it has  a data member, I can use braces on the member initialization list. This is so cool. If I  have an array, I can use braces. Somebody mentioned a lyric. It's supposed to have values of  an array, but that's not the only place. If I have a vector, I can use braces. If I have  a raw freaking pointer, I can use braces to initialize its value. Not that anybody would  use pointers. Uniform initialization. That's the sales pitch. You can use it everywhere.  I don't use the term uniform initialization. And the reason I don't use the uniform initialization  is because that suggests it's uniform. You should use it everywhere. And I don't believe  that that is reasonable advice. I think there's a lot of advantages to what's known as uniform  initialization. However, I prefer to refer to it as brace initialization. That's what  I'll be calling it from now on. So, first thing about brace initialization is it has  the unique feature which is that narrowing conversions are illegal. Only brace initialization  will reject narrowing conversions. A narrowing conversion basically means that the compiler  cannot guarantee that the value of a larger type will be able to be stored in a smaller  type. So, as an example, if I have a point which has two integers and I say point P1,  curly brace 1, 2.5. Now, in C++98, this was completely fine code. The 2.5, which is a  double, would be truncated to initialize Y. So, in C++98, that compiles has well-defined  semantics. In C++11, this code will not compile. And the reason it won't compile is because  I'm using braces and this is a double and that's an int. And 2.5, last I checked, cannot  be exactly represented as an integer. Code won't compile. So, if you want it to compile,  you can make it compile, you just do a cast. All the cool kids use static casts. C casts.  But it's important to understand this notion of rejecting narrowing conversions exists in  exactly one place in the language. Only brace initialization. So, if I say int A is 2.5,  this used to compile because it goes back to the days of C and it still compiles. It'll  truncate to 2 and initialize your integer. No warning, no error. Well, no error. Might  get a warning. But if you try to do exactly the same thing using braces, the code will  be rejected. Given that narrowing conversions normally don't make any sense, like I want  to store a double as an int, an implicit truncation, I think it's a feature that braces don't  let you do that. That's a plus. We now have two different syntaxes for calling constructors.  So I can say, widget w1 and I pass some arguments in curly braces, widget w2 and I pass arguments  in parentheses. They both work, usually. Both choose the best match constructor. However,  there are different rules for what it means to be the best match. Now, this is something  that Nico talked about in his last presentation. Only brace initialization matches initializer  list parameters and those matches are actually preferred. Let me give you an example. So this  is a simplified declaration from the standard library, from the standard vector class. So  here's vector. It takes a number of elements and a value. It takes an initializer list  of t. This is not affected by the change that Nico was talking about at the end of his last  talk. So if I say v1 with 100, 5, notice that I'm using braces. Now, because I'm using braces,  I could pass the 100 is the size and the 5 is the initial value. Two arguments. That  would work. Or I could say 105 are two initial values to go into the vector. There is, in  some sense, ambiguity here. But the language rules say when you use braces and it is possible  to call an initializer list constructor, that is the one that you call. It's not ambiguous.  So this means that the size of the vector is 2 and the values are 105. It calls this  second constructor. If I take exactly the same code and I say, you know, I'm a traditionalist,  I like the old ways. And the old ways of passing constructor arguments was to use parentheses.  So exactly the same code. But now I use parentheses. Under these conditions, now it calls the first  constructor because initializer list constructors are only considered if you use braces. If  you don't use braces, they're not even considered. So the result of this is a vector whose size  is 100 and values is set to 5. All of them are 5. So it does something different. They're  not interchangeable. Now, this only makes a difference if you actually have an initializer  list constructor. So here's a gadget class. It has one constructor. It takes two integers.  So if I say I want to create a gadget G1 with 10 and 20 or a G2 also with 10 and 20 braces  and parentheses, they do exactly the same thing. They both call this constructor. Makes  no difference. They do the same thing. If I now say G3 with 89.5 and 0, notice that  I'm using parentheses, compiler says, okay, 89.5, that's a double. I'm going to pass it  to an int. No problem. I'll take that 89.5, get rid of that pesky 0.5. We didn't want  that half of a number anyway. Compiles. But if I do the same thing inside curly braces,  it won't compile. And the reason it won't compile is because this double can't be expressed  in that integer exactly. Again, you get brace initialization, rejects narrowing conversions.  Yes?  Is the question, does this also apply to larger integer types going to smaller integer types?  Correct. It does. Essentially, the way it is is the only time  that if you have a larger type going to a smaller type, the only time that that will  be permitted to compile is if the compiler can prove that it will fit as an example.  If I said I want to initialize an int and I said the initial value is going to be 22  long, so long is bigger than an int in general. But we know that the value 22 will fit in  an int. That will compile. On the other hand, if I said long L equals 22 and then initial ize the int with the long in curly braces, that won't compile. Make sense?  Yes.  Okay. So basically, it doesn't compile unless the compiler can guarantee it's okay.  Was there a question?  What happened to the specter, your example showing us that there is no specter in the  case of the initializer list.  Okay. So all right. Is your question, what would happen here if we did not have the initializer  list constructor? Okay. If we didn't have the initializer list constructor, they'd both  call the first constructor. They do exactly the same thing. That's essentially the same  as this example here. All right. So now, this leads to some really interesting situations.  So I got tired of using widgets. I've been talking about widgets for 20 years. I'm moving  on. I'm progressing. I'm growing. Let's talk about thingies. So I got a thingie, takes  an int and an int, and thingies take initializer list of double. So if I say parentheses with  10 and 20, it calls the first one. Remember, with parentheses, it will never call an initializer  list constructor. It's very simple. It won't call it. If I use braces here, notice that  these are integers, and x and y are integers, and this is an initializer list of double.  It's going to be a better match to go from two ints converted to doubles than two ints  calling x and y. If there is a way to make the initializer list constructor work with  braces, that's what it'll get called. If I have T3, 89.5 and 0, this is a matter of  type conversion. So this will call number one. Notice that I've got a double here. This  is a double. It still won't try to be converted into an initializer list. If you are using  parentheses, the compiler literally pretends as if that constructor, the one with the initializer  list, did not exist. All right. Now, this means that we can get some really interesting  implications if we add new constructors to classes. Now, I want to be clear here. Anytime  you have a class with some interface and you add a new overload to that class, it is possible  that existing code that used to call some function in your class may now start calling  the other overload. That's inherent when you add a new overload. So there's nothing special  about initializer lists here. If you add a new overload to an overload set, code that  used to compile and call one overload might now call the new overload, which is probably  why you added it. So in this case here, I've got a widget. I'm using braces because I've  embraced the uniform initialization lifestyle. This calls widget with 10 and 20. Life goes  on. Life's good. Somebody comes along later. They add an initializer list constructor to  it. Notice that it's an initializer list of float. This code, unchanged, legacy code,  no longer calls this constructor. It now gets converted to two floats because braces really  want to match an initializer list constructor. If there is any way to make it compile, it  will compile. This is neither a good thing nor a bad thing. It's just a characteristic  of the way overload resolution works in C++ and you need to be aware of it. Now, presumably  when you added this initializer list overload, you said anytime anybody is using braces,  I want to treat that as an initializer list. That's not what you meant. You need to have  a little talk with the API designers. So here's our situation. Essentially, we've talked about  this here. So the guideline is to distinguish between parentheses and braces when initializing  objects. Any questions about that? All right. The last guideline I want to talk about then  is to make const member functions thread safe. We have known from the beginning of C++ that  there were two different ways to interpret const nest. There's logical const nest, which  means conceptually nothing changes. And there's bitwise const nest, which means none of the  bits inside an object actually gets modified. So bitwise const nest, this is what compilers  enforce, conceptual const nest, this is what developers should implement. With any luck,  this is completely old news. We've been talking about this since 1995. I've been talking about  this since 1995. My God, what happened to me? 1995, never mind. All right. Mutable. Also  available since 1998. Mutable, as I like to put it, tells the compiler, ha, ha, I was  kidding about the const. So a mutable data member is permitted to be modified even inside  a const member function. It is very useful when implementing conceptual const nest in  particular for doing things like caching, which is probably its most common use. So here  I have a widget class. It has a member function called magic value, which computes a magic  value. So computing the magic value conceptually does not change the widget. If I get to a widget  and say, so what's your magic value, it goes crunch, crunch, crunch, crunch. Here's my  magic value, but it didn't change the widget. It's the same widget it was before. If, however,  we assume that it's computationally expensive to determine what the magic value is, we would  prefer not to have to compute it unless somebody asks for it, which means we don't want to  compute it up front. But if somebody does ask us for it, we don't want to have to compute  it multiple times because it's expensive, which means we'd like to cache it if we ever  do compute it. So we can implement it like this. We could say, all right, I'm going to  create a mutable bool called cache valid and a mutable int cached value. What I'm going  to do is if I ever compute the value of the magic number, I will store it and then I will  say that the cached value is valid. So I can look it up again in the future, save myself  some time. So here's my code. If the cache is valid, great, I just return the cached  value. Now, if the cache is not valid, maybe because nobody's ever called this function  before, then what I do is I say, okay, the cached value is, I call some expensive computation,  I say the cached valid is now true and I return the cached value. Perfectly standard way of  implementing a cache using mutable. In C++ 98, there's no C++ 11 here. Works fine. Life's  good. C++ introduced this tiny new feature. It's called concurrency. It has some minor  implications for programmers. For example, nothing works anymore. A few things work.  But as an example, let us suppose that I've got some widget W and now in thread one, I  say auto v1 is W magic value. Crunch, crunch, crunch, crunch. It's figuring out the magic  value. Thread two at the same time calls W magic value. Crunch, crunch, crunch, crunch.  Just doing the same thing. Now, I want to go back a slide and show you that magic value  I have declared to be a const member function. It does not change the conceptual state of  the widget. You should be able to have multiple people reading a widget simultaneously. They're  all readers. And because I declared it const, because it is logically const, I have declared  these two data members mutable. The problem I have is that in this case here, here's thread  number one, it's calling a const member function. It's a read operation. Here's thread number  two. It's calling a const member function. It's a read operation. If I know that I'm  writing code with multiple threads simultaneously reading something, that does not require synchronization.  I don't need to get a mutex or anything else if all my threads are readers. It's safe to  read things simultaneously. It's conceptually safe. The problem is that magic value, in  case you've forgotten, that was the previous slide, those look like write operations to  me. The const member function is conceptually const. It fulfills the conceptual const requirement.  The problem is it's not bitwise const. And that makes a big difference when it comes  to concurrency. So this means both of the reads may actually write the mutable data  members, which means we have two threads which are both readers and writers. That's the definition  of a data race. That means undefined behavior. And that's just bad. What's key here is there's  nothing wrong with the calling code. Both threads are performing read operations. There's  nothing wrong with those threads. They're doing perfectly reasonable behavior. Possibly  the code was written so they would do a lot of reads. So you can't blame them. Well, if  the client code's not wrong and the behavior's undefined, that pretty much means the implementation  code is wrong. No other way to look at that. Widget magic value is a broken function. Now,  fixing it is really easy, and this is where C++11 comes in. So all we have to do is, okay,  I'm going to slap in a mutex, and I'm going to declare the mutex to be mutable because  locking a mutex changes its state. It's a non-const operation. Unlocking a mutex also  changes its state. Non-const operation. So I need to perform non-const operations on  the mutable inside the const member function. Sorry, on the mutex. So no problem. I lock  the mutex at the beginning of the function. I perform the operation to figure out what  the value is, and then this object in its destructor will automatically unlock the mutex,  so this closed curly brace here unlocks the mutex. This works fine. This fixes the problem.  Now, in some cases you don't need a mutex. In some cases you might only need an atomic  variable, which is a variable where operations on it are guaranteed to be viewed atomically  by other threads. For example, let's suppose I'm keeping track of how many times the function  is called. So here's my gadget class. Here's a get-wait function, and for whatever reason  I want to know how often is get-wait called. Under those conditions I could have an int  and I could have a mutex to limit access to the int, but or what I can do, which is a  little faster in many cases, is I can just use an atomic unsigned. So what that means  is that when I call plus plus call count, that's a read modify write operation. It's  guaranteed to be executed atomically, typically through a single underlying machine instruction.  Typically it is faster at runtime than using a mutex. So this works fine. So notice I'm  not saying you should always use a mutex. This doesn't need a mutex. However, if we  go back to our example with the cache and we say, okay, I'm going to make the cached  value an atomic int and the cached valid an atomic bool, this is the way of thinking it  says, well, if atomic is cheaper than using a mutex, I'm going to use them all over the  place. I'm a C++ programmer. I don't pay for what I don't need. So here's the code again.  Now in this case I've rewritten it a little bit differently here. So I check the cache  to see if it's valid. If so, I return cache value. Otherwise, in this case, I perform  an expensive computation number one, which I store as V1. I now perform an expensive  computation number two, which I store as V2. I now say, okay, the cache is valid. That's  true now. And I return the cache value, which is V1 plus V2. So I've just broken the expensive  computation into two pieces. It's all I've done. This is not thread safe. Why not?  Let me get in my spot. Okay. Now I can see you. Yes.  Each of the lines are atomic, but the whole transaction is not atomic.  Okay. You are correct. Each of the lines is atomic, but the transaction is a whole, excuse  me, the whole thing is not transactional. The problem is that one thread can come in,  see that cache is not valid, start doing the expensive computations, and then can say cache  valid is set to true. In the meantime, another thread comes in and goes, oh, the cache is  valid because the other thread just set it to true. So it now returns the cache value,  but we haven't computed the cache value yet because the first thread hasn't yet performed  the sum operation. Oops, wrong answer. Only happens once every five years or so. So we  don't really care. We can fix that operation, you say. No problem.  What we'll do is we'll compute the cache value before we set cache valid to true. Right?  That'll fix the problem. It will fix the problem. You'll get the right answer. This works.  It just works harder than it should. Thread comes in, checks to see if cache valid is  true. It's not. Starts performing the expensive computations and then adds V1 and V2 together.  At this point, we've performed the expensive computations. We are ready to go. Another  thread comes in, checks to see if cache is valid. It's not yet because we haven't yet  executed this statement in the first thread. Enters the loop, starts those expensive computations  all over again. In the meantime, 22 other threads also check cache valid simultaneously  because you are running on a 48 core machine. And they are now all performing the expensive  computations. You are really going to get the answer now. Yes?  So the question is, won't each thread get its own value? If we proceed on the assumption  that there is only one magic value for the widget, in other words, no matter how many  times you compute it, it will always get the right answer, every thread should ultimately  get the same answer. It's just that we're going to compute those sub-computations every  time. Let me think for a minute. Let me put it this way. That's the best case scenario.  Okay? Let me check and see. Okay, in this particular case, because they're both atomic, I don't  have to worry about partial results being read by thread, so I believe that what I said is  true. But even if this does return the right answer all the time, it's much more expensive  than it needs to be. So, you know, the problem is if you have more than one data member and  they need to be kept in sync somehow, you need a transaction. If you need a transaction,  almost certainly what you want to do is use a mutex. So declare the mutex as I showed  you before. Now cache value and cache valid, these don't need to be atomic any longer.  Now atomics are more expensive than non-atomics, cheaper than mutexes, but more expensive than  non-atomic. So these can now become regular, ordinary, instant bools because we're now  using the lock guard solution. So if you have a const member function in C++11, the first  version of the language that supports concurrency, it should either be bitwise const or it should  be internally synchronized, meaning that if it's doing read and write operations, outside  callers can call it safely without having to acquire a mutex. If you find yourself in  a situation where you know for a fact that your class will never be used concurrently,  you just know that. Simplest way to know it, you're writing a single threaded application.  There's no other threads. Under those conditions, then using atomics or acquiring and releasing  a mutex is incurring a cost you do not need. If the logic of your program is such that  we know that there will never be for a particular kind of object two threads using it simultaneously.  For example, if I have a phased design and in the first phase of the design, only one  thread uses the object and in the second phase, only some other thread uses the object, but  they never access the object simultaneously. If you know that, if by design of your program,  you guarantee that they will never be simultaneously accessed under those conditions, that's an  exception to the rule. So if you are writing purely single threaded applications, you don't  consider think at all about threading, you can forget about this guideline, program in  C++98. But if you are programming a context where it is possible for your classes to be  used in a multi-threaded environment where there could be simultaneous readers, then  you need to follow the guideline that I'm giving you, which is to make constant member  functions thread safe. Any questions about that?  So the question is how do you ensure that if you are using a third party library that  they have basically done this. If you find a third party library that's not doing this  correctly, you need to file a bug report with the library. I mean, it's just, it's  a concurrency bug in the library. So you may have to pay the cost of the library.  So you may have to pay the cost of discovering that as a bug. I mean, essentially in moving,  in going from a world with only one thread to a world with multiple threads, it is not  going to be surprising if third party libraries are going to have some concurrency errors in  them. Yeah.  In your previous example, the mutex, you could use the mutex inside the else clause.  So the question is, can't I move the mutex inside the else clause? And the answer is  no. Because otherwise, then I'd be reading cache valid outside the mutex.  Okay. So essentially the question is, all right, I want to move the lock guard inside  the else and then I'm going to make cache valid atomic so that my threads, trust me,  you're still going to get in trouble. In particular on relaxed memory architectures. This leads  us right down the road into memory barriers and stuff like that. And that is a hell I do  not want to go to. So I'm afraid I'm going to have to cut you off if you're asking more  questions about that. I have a laser. I'm not afraid to use it.  Okay. So here's the summary of what I want to talk about. We talked about three guidelines,  but I slipped an extra one in there. So prefer auto to explicit type declarations. It's not  just about programming style. It's also about efficiency and correctness. Remember that auto  plus a brace initializer will deduce an initializer list. Distinguish parentheses and braces  when creating objects. I'd love to be able to say always use one or always use the other.  It's not that simple. I'm sorry. Make const member functions thread safe. If you're looking  for more information about this, there's a true work of literary genius in the works,  I hope. I'm working on a book called Effective Modern C++. I hope it's going to be out in  October. If you want other guidelines based on information that will probably be in the  book, I gave a presentation last year called an effective C++ 1114 sampler. It covers different  guidelines than what I talked about here. Herb setter has written a thing called elements  of modern C++ style. So you can get some guidance from.
Scott_Meyers_-_Effective_Modern_C_0_47.png Scott_Meyers_-_Effective_Modern_C_48_2723.png Scott_Meyers_-_Effective_Modern_C_2724_5039.png Scott_Meyers_-_Effective_Modern_C_5040_5963.png Scott_Meyers_-_Effective_Modern_C_5964_6491.png Scott_Meyers_-_Effective_Modern_C_6492_6539.png Scott_Meyers_-_Effective_Modern_C_6540_7847.png Scott_Meyers_-_Effective_Modern_C_7848_9443.png Scott_Meyers_-_Effective_Modern_C_9444_12719.png Scott_Meyers_-_Effective_Modern_C_12720_18275.png Scott_Meyers_-_Effective_Modern_C_18276_19271.png Scott_Meyers_-_Effective_Modern_C_19272_22919.png Scott_Meyers_-_Effective_Modern_C_22920_25499.png Scott_Meyers_-_Effective_Modern_C_25500_26027.png Scott_Meyers_-_Effective_Modern_C_26028_26075.png Scott_Meyers_-_Effective_Modern_C_26076_26159.png Scott_Meyers_-_Effective_Modern_C_26160_26207.png Scott_Meyers_-_Effective_Modern_C_26208_26567.png Scott_Meyers_-_Effective_Modern_C_26568_32627.png Scott_Meyers_-_Effective_Modern_C_32628_37655.png Scott_Meyers_-_Effective_Modern_C_37656_43115.png Scott_Meyers_-_Effective_Modern_C_43116_45707.png Scott_Meyers_-_Effective_Modern_C_45708_47519.png Scott_Meyers_-_Effective_Modern_C_47520_48251.png Scott_Meyers_-_Effective_Modern_C_48252_48299.png Scott_Meyers_-_Effective_Modern_C_48300_49187.png Scott_Meyers_-_Effective_Modern_C_49188_49343.png Scott_Meyers_-_Effective_Modern_C_49344_49451.png Scott_Meyers_-_Effective_Modern_C_49452_49655.png Scott_Meyers_-_Effective_Modern_C_49656_49823.png Scott_Meyers_-_Effective_Modern_C_49824_49883.png Scott_Meyers_-_Effective_Modern_C_49884_50219.png Scott_Meyers_-_Effective_Modern_C_50220_50783.png Scott_Meyers_-_Effective_Modern_C_50784_53555.png Scott_Meyers_-_Effective_Modern_C_53556_54347.png Scott_Meyers_-_Effective_Modern_C_54348_55883.png Scott_Meyers_-_Effective_Modern_C_55884_56867.png Scott_Meyers_-_Effective_Modern_C_56868_57479.png Scott_Meyers_-_Effective_Modern_C_57480_59987.png Scott_Meyers_-_Effective_Modern_C_59988_60683.png Scott_Meyers_-_Effective_Modern_C_60684_60755.png Scott_Meyers_-_Effective_Modern_C_60756_60803.png Scott_Meyers_-_Effective_Modern_C_60804_60851.png Scott_Meyers_-_Effective_Modern_C_60852_61451.png Scott_Meyers_-_Effective_Modern_C_61452_61499.png Scott_Meyers_-_Effective_Modern_C_61500_62195.png Scott_Meyers_-_Effective_Modern_C_62196_62951.png Scott_Meyers_-_Effective_Modern_C_62952_62999.png Scott_Meyers_-_Effective_Modern_C_63000_64475.png Scott_Meyers_-_Effective_Modern_C_64476_65675.png Scott_Meyers_-_Effective_Modern_C_65676_65759.png Scott_Meyers_-_Effective_Modern_C_65760_65903.png Scott_Meyers_-_Effective_Modern_C_65904_66251.png Scott_Meyers_-_Effective_Modern_C_66252_67283.png Scott_Meyers_-_Effective_Modern_C_67284_70283.png Scott_Meyers_-_Effective_Modern_C_70284_71291.png Scott_Meyers_-_Effective_Modern_C_71292_71879.png Scott_Meyers_-_Effective_Modern_C_71880_72791.png Scott_Meyers_-_Effective_Modern_C_72792_73247.png Scott_Meyers_-_Effective_Modern_C_73248_73571.png Scott_Meyers_-_Effective_Modern_C_73572_74351.png Scott_Meyers_-_Effective_Modern_C_74352_75707.png Scott_Meyers_-_Effective_Modern_C_75708_77315.png Scott_Meyers_-_Effective_Modern_C_77316_80015.png Scott_Meyers_-_Effective_Modern_C_80016_82319.png Scott_Meyers_-_Effective_Modern_C_82320_82523.png Scott_Meyers_-_Effective_Modern_C_82524_82739.png Scott_Meyers_-_Effective_Modern_C_82740_83531.png Scott_Meyers_-_Effective_Modern_C_83532_86399.png Scott_Meyers_-_Effective_Modern_C_86400_87719.png Scott_Meyers_-_Effective_Modern_C_87720_88883.png Scott_Meyers_-_Effective_Modern_C_88884_88955.png Scott_Meyers_-_Effective_Modern_C_88956_89399.png Scott_Meyers_-_Effective_Modern_C_89400_89459.png Scott_Meyers_-_Effective_Modern_C_89460_89735.png Scott_Meyers_-_Effective_Modern_C_89736_90375.png