CppCon 2017: Bryce Adelstein Lelbach “C++17 Features (part 1 of 2)”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
👍︎︎ 1 👤︎︎ u/syaghmour 📅︎︎ Nov 16 2017 🗫︎ replies
Captions
- So as I mentioned, I'm Bryce. I work at, now NVIDIA, on this library here. Come talk to me about Cuda stuff later, if you feel so inclined. And today I'm going to be talking about C++17. As my little punch line here says, an introduction to C++17 via inspiring examples. So this talk, I've given a few times before in different forums, and a number of people contributed material in some way, so, this whole list of names here, and you can find the talk right now if you'd like at this URL. It uses this weird bundle of JavaScript called reveal.js, which is kind of nice. It's my first time using this for one of these talks. So these are the 26 features we're going to be talking about today. So this is a two part talk, so this first one hour session, we will hopefully cover all of the language changes, and then, the second one hour session, we will cover these library changes right here. And the ones that are underlined, are sort of those that I think of as more high impact; whereas, the ones that aren't underlined are sort of features that are there, and that are nice, but maybe not as widely useful. So, let's start off by talking about the destructuring problem. So that's, like how do we, like we have some point 3D class, and how do we assign convenient names to the components of this class? So that's this structuring problem. So for example, with, like we have this here, and we want to do something like this, and there's a bunch of different, sort of caveats here, like we may want those names to have reference semantics instead of value semantics. Now you can sort of kind of do this with tuples in C++11 using std tie, which you may be familiar with. And there's kind of a number of problems with this. Because the variable names need to be separately declared before they're bound here. So I've got my double x, y, z declaration, and then I tie them down on the next line there. So for example, you would not get any warning if you had repeated names here, like I'd accidentally written std tie y of y, z, when I really meant std tie x, y, and z. And if I wanted to bind a reference, you can't really do this because I can't have uninitialized references. And you also, if you wanted to capture the const variables, the double const here, you can't have an uninitialized const, so this doesn't really work in the way that we would intend it to. More generally, this pattern won't work with any types that are not default constructable, because they have to be separately declared before you tie into them. And also, of course, tie will not work with your struct, or things like std array. You might also want to be destructurable. So in C++17, we have a new language feature called structured bindings, and it allows you to take things that are destructurable, so something like my point 3D class or a tuple or an array, and create variables that represent the components of that object, and give them convenient names. So the syntax is auto, and then you have square brackets, a list of names, and then, the destructurable object on the right hand side. So when I say destructurable, the requirements is that, either all the non-static data members of the class must be public, direct members of the type, or members of the same public base class of the type, and cannot be anonymous unions. Or, for any other types, so some things that's like class, there's private data, you can give it a .get method, or you can have a get global function that will match with that class high ADL. And then you also have to specialize these other two customization points, tuple size and tuple element. So in the standard library, there is I believe only these three types that meet these requirements. The std array, std tuple, std pair. And the auto in this syntax uses the regular auto deduction rules, so like you can write like auto const, or auto ref, auto ref ref. And that just sort of works in the same way that auto does today. So there's no real caveats there. So this can be particularly useful when dealing with things like std map, which gives you back a pair, which is kind of annoying to work with. So this is, right here, it's maybe a little bit hard to read, but I've got a range based for, when I have auto ampersand ampersand, and then key value, and then the map on the right here, so I don't have to deal with any pair's ugliness, which is quite nice. Here's a, are these in the wrong order? Oh, no, this is before and after. So, this is the next feature I'm going to talk about which is also sort of about std map. This example is, so we often end up introducing variables right before an if, even if we're going to use the variable only within that if statement because the if condition is a variable, so like right here, I introduced this auto it in the outside scope, but I'm only going to use it within this, within the if condition within the statement associated with the if condition. Now in C++17, there is a new feature called selection sequence with initializers. So it's sort of similar to the syntax you're familiar with for for statements, and then you can do if, and then an initialization statement, and then semicolon, the if condition. So I take that iterator that I created here, and I can just initialize it right here, and then it will be available in this scope right here. So the syntax looks like this. So if init condition, then you have your two statements here and what it's equivalent to is putting that init into a, right before the if statement in a new scope. So this is just like a little syntax hack. One thing to keep in mind here is that the variables and the initializer are in scope for all possible branches following the initializer. So in this example here, I have an if init condition saved in here where I introduced X. So X is in scope here. Then I have this else if where I introduce Y. And both X and Y are in the scope here. And then in the last else here, both X and Y are in the scope. It's a little unintuitive, but like if you expand out this, you can see why it works like this. So this feature can be very useful when dealing with locks. Like, suppose I'm trying to implement this class string pool, which is supposed to be a thread safe, sort of, like, hash of just strings that have memory associated with them, so that we don't have to go and allocate new memory. So we want to implement the pop method for this string pool, and so we implement it like this. So, first we have default constructed strings here, then we go and try to acquire the utext, and if the pool is empty, if the pool is not empty, we go and swap the last string in the pool with this temporary string we've created, and then once we are outside of this if statement, we go and check if the capacity of the string is smaller than the capacity that we've requested. And if we do, we go and ask the string that we just got to reserve a new one, so if we didn't find a string here, this reserves the desired capacity, and then we return here. So there's a bug here of course, there's some performance bug in that. We are holding this lock, while we're doing the reserve here, and that's not good because that might be allocating memory. You don't want to hold a lock while you're allocating memory. So to fix this, you would put a scope around this lock guard and this if statement here, so that the lock is released before you reach this branch and the possible reserve right here. But it's kind of ugly because you have to introduce, you know the extra squiggly brackets. I really don't like having more squiggly brackets than I need in my code. It makes it harder to understand. So with this new syntax, you can just put the lock guard right in that initiate, right in that if statement here, and the scope of lock guard is just right here. It'll be this word right here. The lock hoist. And last one of these. This I think combines very nicely with structured bindings, when you're working with maps. So like right here I've got this in place for throw functions, so I want to try to put something into this set, and if it's already in the set, then I'm going to throw, otherwise I just want to put it in there. And so I have this if and then the structured binding right here. So that introduces that for the scope here, and then we would go, you know, based on whatever the key is, we could create an exception, and say hey this existing key was in the set that you just tried to insert into. So this syntax is also there for switch. So you can do switch, init, condition, and it's sort of the same mapping right here. And I don't really have an example for this one, so this one's a little bit less frequently used, it was difficult to come up with a good example for this, but it is there if you need it. Alright, so next I want to talk about if constexpr. So before C++17, when processing variadic parameter packs, we would often implement an interface like this, where I want to say, hey, here's a, you know, a parameter pack of things I'd like you to print. And we would implement this by having a base case, and then a recursive case. So anybody who's starting any amount of pertinent variadic parameter packs is probably familiar with this pattern. In C++17, we can write this in just one overload using if constexpr. So we don't have to worry about the base case recursion, because what if constexpr does here is, if this condition's not true, then this statement is, needs to parse, but is not instantiated. So the fact that there is, that when you're on the last element here, but there's no function overload that matches over here is fine because this was never instantiated. Now if you didn't have the if constexpr here, you would be trying to call print with zero arguments when this parameter pack is empty, and there's not an overload for that so you get a compiler error. But with if constexpr, you don't get the compiler error, you just never instantiate. Syntax is this, and at some points in time, this has been called constexpr if because the syntax used to be the other way around, but that got complicated because then you needed to have additional conxtexprs, when you load else branches, so it's a little unintuitive if you read some of the early papers about this feature, and some people still call it constexpr if, but rather if constexpr. So the condition must of course be a constexpr expression, and the statements are discarded if the branch is not taken. So they can use variables that are declared but not defined, and discarded templates, discarded statements and templates are not instantiated. So, yeah so, we already talked about this. So this can be used to replace SFINAE in a number of places, so if you're writing make unique before C++11, you would need to use SFINAE because you need to handle two cases. One where the type T is constructable, and you're going to use, you're going to do new T parentheses, the list of arguments. But if it's not constructable, you need to do brace init. And so you just have this SFINAE constructible, not constructible. Now in C++17, you can just use if constexpr here, and if it's constructible, only this statement will be instantiated. If it's not, then only this one will be instantiated. And you can do some more advanced things with this, like, if you're familiar with, I'm sure advance from the standard library. So that takes an iterator and advances it some number of elements. And usually this is implemented with tag dispatching. So there's different implementations that you'd want based on, depending on the type of iterator that you have, and so you go and figure out what is the iterator category of the iterator I've been given, and then I'll go call this other function which, with an instance of this iterator category type, and overload resolution will point me to the correct implementation. In C++17, we can just use if constexpr here to write this in one overload, and simplify the logic so that you don't have to think about the overloading here, you can just sort of follow the control flow. If it's this type of iterator, you do this, if it's this type of iterator, you do this, et cetera, et cetera. Yep. (audience member asking question without microphone) It's a little hard to see back there. Gotcha, sorry. Yeah, I will do my best on that, unfortunately I have not checked whether, how it was going to show up on this screen. My apologies. (audience member asking question without microphone) Is there any way we can kill the lights? (audience member asking question without microphone) Okay, that would be great, yeah. (audience member asking question without microphone) Yep, they're on, the first slide had the link. I can go over it, pull that up. (audience laughing and chattering) Hang on. Okay, the link's right there. I think that one's a different shade of blue. So I'll leave that up for a moment, let you guys grab that. (audience member asking question without microphone) Yeah, that would be awesome. If you can just turn them off, that would be great. Alright, sorry about that guys. So we definitely have plenty of time, so I can wait a couple minutes to see if they can shut off the lights before moving on, or I can just keep going. Sort of up to you guys. Keep going, you want? Okay. Let's see where we were. It's getting there. Okay. Alright, so just refresher for where we were. We're talking about if constexpr. We just did this example with advance showing how we could replace tag dispatching with if constexpr. So let's say that I have some sort of, like person struct, like this, where I've got three data members, and then I've got three getter functions. And suppose I wanted to implement a global get function to non intrusively make this struct destructurable, so that it would work with structured bindings. So in C++11, I could do this, is this better colorwise? Okay, awesome. In C++11, I could do this by having, you know, this is my, well my get function's signature function's going to be, declared here, and then I could explicitly specialize it for the three cases. C++17 with if constexpr, we can just write this once here. You'll notice that there's no static assert or SFINAE to check for the out of bounds case where I is greater than two. And that's because in that case, a return type of void will be deduced here, and a void ampersand, so returning void reference, is going to be an error because it will try to instantiate void, and so that would not compile. And so that sort of SFINAE's it out. Alright. Next, yep. (audience member asking question without microphone) - Yes. I believe yes, I believe so. Yeah. So next up, we're going to talk about fold expressions. So in C++11, if we wanted to write a variadic function like this, like this auto sum here, so it takes a bunch of arguments, and then it adds all of them together. We might write it like this, where we, again, we're gonna have a number of overloads, we're gonna have sort of some base cases, and we're gonna have a recursive case here. In C++17, we can write this in just one case using what's called a fold expression, so it's a new way of working with parameter packs. So I've got here, this, in parentheses N-S plus dot, dot, dot, plus zero, close parentheses. So fold expressions apply binary operators to parameter packs. There are four types of fold expressions. They differ in the order of application, and whether they take an explicit initial value. So an unary right fold does not take an initial value, and it applies from the right. So it's, we would expand out to this. Left fold would apply from the opposite direction. And then the binary fold, the same thing, except they take an initial value, which is what's given if the parameter pack is empty. So all binary operators are foldable, so this is a list of all of the binary operators in C++. So for this sum that I showed you guys before, where we, what we have here is a binary right fold. So, in parentheses N-S plus dot, dot, dot plus zero plus parentheses, and this would expand here if I did a for argument, is it would be 3.14 plus 1e7 plus 42, negative 42, plus 17, plus zero here, which this is the initial value, and if I gave it just nothing, it would just return zero because that's the initial value. So a boolean and function would be a good example of a unary left fold. So this is going to fold over the boolean and operator, so it'd just look like this. Just dot, dot, dot, ampersand, ampersand, and then the parameter pack. And so I tell you the for arguments here, would expand out to this. So if I call it with no arguments here, what does it expand out to? Well, for these three operators, if the parameter pack is, they have sort of special semantics. If the parameter pack is empty, then the value of the fold is, for boolean and, true, for boolean or, false, and for the comma operator, a sort of void default construction expression. And for any of the operators not listed above, an unary fold expression with an empty parameter pack is ill-formed. So you can do some, here's another little neat one. This is a variadic print function here. It's a binary left fold over the left shift operator with an initial value of std cout. See the initial value's right here, std cout. Then the operator's right here, dot, dot, dot, the operator again, and then the parameter of, the expression that gives us the parameter pack here. And then this is not part of the fold right here. And this for each arg here says takes a function, and then a parameter pack of arguments, and calls the function once on each argument, and this is a unary right fold over the comma operator, and it takes advantage of this. Whoops. It takes advantage of that last special case right here. Yep. - [Audience Member] Actually two questions. On the sum examples, zero is optional, correct? - Zero is not optional because we're summing over the plus operator, and the special cases here, so you need to provide the initial value for operators that are not covered in the special cases here or if the default initial value in these special cases doesn't work for the types that you're, that you're dealing with here. - [Audience Member] Second question for the previous example. - Yep. - [Audience Member] Does it print all our values and then the line or it prints valueless then the line greets you? - That's a good question. So, the fold expression here, which is in parentheses, so this is completely separate from the new line. So it's going to print all of these arguments, and then one new line. Not one of these arguments, new line, one of these arguments, new line. If you put the new line, let's see where did you have to put it? Yeah. I think, yes. That would do it, yeah. If you put it inside of the parentheses here, then you'd get argument, new line, argument, new line, argument, new line. Nope. (audience member asking question without microphone) Ah, right. Yeah, yeah, yeah, yeah. Yeah that would not compile because then the result of that expression would not be a, yeah. Yes. Neke is right, you'd have to do some other tricks to get that. Yeah. Any other questions? Okay. (audience member asking question without microphone) Sorry a little bit louder. - [Audience Member] Folding and, and, and or, or. Do they short circuit? - Yes, the question was, do the folding and, and and or, or short circuit, and yes they do. - [Audience Member] Thank you. - Okay. Next up. Before C++17, we've had function template deduction to simplify the usage of function templates allowing programmers to emit explicit template parameters when they can be deduced from the function's arguments. So, like, we have this std make tuple function, which takes advantage of function template deduction to allow you to create a tuple, where you don't need to explicitly say hey, it's a tuple of int and double. It's just deduced from the arguments to the function. This is not, we've not had this sort of deduction for class templates, until C++17. So now, you can just write std tuple, without any template parameters, and then these constructor arguments here. And then the types will be deduced from the constructor arguments. They can also be deduced in new expressions, so it's probably pretty silly to new a tuple, but it would, if you do new std tuple, and zero, zero here, you'll get a tuple of int, int. (audience member asking question without microphone) No. You cannot partially specialize. Class template deduction, I think simplifies a lot of common patterns from modern C++ like resource acquisitions initialization, so for mutex you can just write std lock guard. You don't have to specify the mutex type. That's kind of nice. So the class template parameters can now be deduced in declarations, function style cast expressions, and new expressions. And they're only performed if no template arguments are provided, and there's no partial specialization. You can't provide one of them, like right here. That's not allowed. There's also a sort of later feature called deduction guides. Because class template deduction doesn't always work as you may wish, so this facility lets you control how class template deduction operates. Now these deduction guides don't have to be templates, and I'll show you guys an example of this. They are not defined inside of a class. They're defined after the class definition, and they have to be within the same scope as the class template. So, these are sort of best understood through examples, I think. So like this deduction guide is in the standard library. So what this, and the syntax is right here, so it's template name, so vector, and then the parameters here, and then a right arrow, and the type that this constructor should deduce to. So this is for vectors constructor that takes two different iterators. If we didn't have this deduction guide, it might, it would try to deduce this as a vector of iterators, and it wouldn't know how to figure out what the value type is, of the iterators. So what this guide says, is hey if you're going to do iterators here, this is how you go and find the the correct value type from an iterator. And so the vector's constructor that takes two iterators, this one here would go and use a deduction guide; whereas, doing a vector from a initializer list would just use automatic deduction. So as I said, they don't need to be templates, so if I have this, like template T here. Struct name, it's got a constructor that takes a, like a first and last here. I could have, oh, there should be character const stars there. I could have this deduction guide, it's not a template, which would point a expression like this to this constructor. Sorry there should be a second char const star there. Okay, any questions on this one? Deduction guides are sort of a more advanced feature. They're a little bit, there's a number of caveats here in how they work. (audience member asking question without microphone) That is, that is the sort of suggested usage, yes. - [Audience Member] What was the question? - The question was, are deduction guides for the class author of the class user, and the answer is they're, they should be used for the class author, but there's nothing that necessarily stops the class user from using it. (audience member asking question without microphone) Yes. (audience member asking question without microphone) Right, but I could go and add this if this deduction guide wasn't in the standard ipo, and add it. The question was, these are put into the same scope as the class, so doesn't that prohibit users from extending third party classes? I don't think it necessarily does. It is, if you want to go and do it, perhaps you could abuse this feature. Okay. So next up, I want to talk about this feature, auto non type template parameters. Oo, that's one too far. Nope, that was correct. So std integral constant is one of the fundamental meta programming primitives. We use it to express compile time values. Something like this. Struct const where it takes two template parameters. The first one is a type template parameter, and the second one is a non type template parameter of that type. And then it just has a static constexpr data member that has that value. So you could use this to express a compile time integer, compile time character, et cetera, et cetera. So in C++17, instead of having to have two parameters and explicitly provide the type of this non type template parameter, we can write template auto, and just provide the non, the literal argument, like some literal integer here, or character, or you know, boolean, or in this case here, like a function pointer, and it will deduce the type. So this is pretty nice because it simplifies expressing compile time constants. You don't have to write things twice. It's somewhat similar to some of the other deduction features I just talked about. So this is also known by some people as template auto. It uses the regular auto deduction rules, so you can sort of constrain it in the same ways that you can constrain auto in any other place. So one thing this is useful for, is that previously non type parameter packs had to be homogeneous. Like you could have this struct sequence, where you say I want to, you know, a list of compile time integers, or a list of compile time characters, but you couldn't say, like I want like a compile time tuple, and now in C++17, you can have something like that by doing template auto dot, dot, dot, so, like right here, this would be my tuple, where I've got an integer literal, or a character literal, and a boolean literal. And then when you're dealing with these types, you can use decl type to figure out which type each element is. So one place where this is useful, some of you may be familiar with span from the guidelines support library. It's a non owning view of a contiguous sequence of objects, so like a pointer plus a size conceptually. The number of elements in a sequence can either be provided at compile time, by just specifying literal, or at run time by specifying the template parameter, this sort of magic value dyn, which, in the sort of C++11 to 14 era span would just be a constexpr global variable that has some magic value. That's of type std size T, but is some value that presumably nobody would ever provide. Like negative one. And in C++17, instead of having this magic value, that's you know, actually a std size T, we can create some trivial type here, and use it as a tag type, and use template auto instead of std size T as the second parameter here. And then in this second case, when we write dyn here, we can use decl type inside of span to distinguish between this tag type, that indicates something, in this case that the length of the span will be provided at run time, via constructor to span. Or the std size T compile time literal. Speaking of tag types. C++17 addresses one of the major pain points with meta programming. C++17 chooses to inline variables. So, now, like I can mark this global constexpr, this global variable as inline, and it's actually implicit for constexpr data members, but it is required here. And then I can use this library in multiple translation units without having to deal with odr violations. So variables can now be inline just like functions. They may be defined in more than one translation unit as long as the definitions are identical. The definitions must be present in a translation unit that accesses the inline variable. And these inline variables have external linkage, so they're not static, and they must be declared inline in every translation unit, and they have the same address in every translation unit. And a static constexpr member variable is implicitly inline. So, like this is quite nice. Like before, if I wanted to have some global atomic bool as a flag to indicate that something's ready within some toy project I'm working on, I would need to have it in the header, and have it extern, and then in the source file initialize it, but now you can just write inline std atomic bool ready, initialize it, and put that all in the header, and that's fine. I mean, you use multiple translations. And also, we could stick this inside of a struct, and make it static and still be fine, just mark it inline. Oh, that was the last one there. Alright, next up, I'm going to talk about two different features relating to using lambda in constexpr context. So before C++17, it's been a little difficult to use lambdas in constexpr context. So if I just write a lambda like this add lambda here, and I try to call it in this constexpr expression here, to construct this int I, I'll get a compile error because the call operator of the synthesized lambda is not constexpr. In C++17, we don't have this restriction. You can, there's now a syntax where you can indicate that the lambda should be constexpr, but it's actually been made implicit, so you can write this now, but you don't need to. This will just work out of the box. And the reason here, is if the compiler can just figure it out because it has the definition available because the lambda's written right where it's used. It's not, never going to be forward declared and then used somewhere else. It's always going to be visible. You -- (audience member asking question without microphone) well, but how would you declare a lambda constexpr? (audience member speaking without microphone) Oh, yes. You would get an error. If you write this, and it's not actually constexpr, you would get an error. If you did something that's not legal in a constexpr context inside this lambda, you would get an error; whereas, if you wrote this, and you did something here that you couldn't do in constexpr you would only get an error here when you tried to use it in the constexpr context. Yes? - [Audience Member] What is the type of the lambda itself? When you mark the constexpr, does it change the actual function object? - The question was, does it change the actual function object? Can you clarify in what way? - [Audience Member] An object that has a callable operator, and isn't a name for callable operator itself, a constexpr function? - Yes. So all it does is that the synthesized call operator of the lambda is now synthesized if it was marked constexpr, as if you wrote a function object with the constexpr call operator. Let's see, you can also create constexpr lambdas. So here this is a lambda with a constexpr call operator, but this is a constexpr lambda. The distinction here, is that this lambda you can call it, it's call operator is marked constexpr, but its copy constructor is not. Its constructors are not. Whereas this one, is being, does have a constexpr constructor and copy constructor. And this is of course relevant if you have captures and your constructors and copy constructors and assignment operators are not trivial. So Louis Dionne has an interesting use case for this, which is that he can use it as a way to implement an efficient tuple, where, sort of, you do it recursively, and use captures. He gave a talk at C++ Now, I believe, C++ Now 2014 about how to do this. Alright. So before C++17, static assert required you to give, to give it two arguments. The first of the expression, and in the second, a failure message. Unlike assert, which just requires one. So a lot of people like me got into the habit of often writing in static assert, something like is addable V T, and then just an empty string, because we didn't want to think up a particular assertion message. We just wanted to get the assert fired. If we made a mistake, and then it wasn't really something that we were using, that we were expecting users to run into, it was just something internal, we didn't really want to provide a message. So now, static assert has two forms. It can either take a message, or it can not take a message. So this is a small feature, but it means you don't have to explain to your coworkers why you've written this. So also in C++17, return value optimization, which is performed by a very C++ compiler, more or less, wasn't required before 17, and now it is required. So previous, prior to C++17, you could not write something like this grab lock function here where you do, where it takes a std mutex, and then it constructs this temporary lock guard and returns it. The reason you couldn't write this is because even though it's very clearly going to be rvo'd, you'd get a compiler error because the copy or move constructor would be required and lock guard has neither. So in C++17, rvo is now guaranteed, so the compiler does not enforce that you have these constructors that would never be needed. So this is nice for writing this sort of factory function, where you have a type that is not copy or move constructible but it is somewhat of a small feature. Now one thing to note is that only rvo is mandatory nrvo, so named return value optimization is not. So it's only this case, not the case where it's a named variable, like if it was one of the arguments here that you were going to return. Anything that's nrvo is not guaranteed, and would still have these requirements. Alright. So this is the, we've got one or two more, so we'll probably break a little bit early. In C++17, you can now declare nested namespaces, so instead of doing Namespace A, B, C, you can just do namespace a, colon, colon, B, colon, colon C so again, this is just a little syntax hack. And then, we've got this new pre-processor predicate called underscore, underscore has include, and this can be used to test if a header include exists. So like right here, I'm doing pound if, has include so if I have a string view available, then go ahead and include it, and otherwise, if I have experimental string view available include it, and otherwise, just define this have string view to zero here. And so what this does is it just like, it searches the header, it searches for the header but does not include, does not actually include it into the file, so it allows you to test for whether a header exists. Pretty straightforward. Okay, so, yep. (audience member asking question without microphone) So the question was, how does this work with different versions of C++? So the example you gave was that if I was compiling, trying to use string view, but using C++14. This is a good question. I am not certain whether this is something that, that would be backported to compilers. I mean, obviously if you're using a compiler that's older than this feature, it probably is not going to be around. (audience member asking question without microphone) So the question was, if C++20 introduced a header, that was not in C++17, would this feature find it (audience member asking question without microphone) when compiling in C++17. I suspect that's probably. I'm not sure I have an answer for that. Let me get back to you after the break. I'd have to look that up. I am not sure. (audience member asking question without microphone) My guess is that it's probably implementation defined. Or -- (audience member asking question without microphone) You can't say if def, because this is not, this has include is not a pre-processor definition. It's like this pre-processor, it's a pre-processor predicate, so it needs to be pound if, not if def. (audience member asking question without microphone) Right, okay, so Mihar pointed out so CPP reference indicates, it only checks whether you can include a file, so like whether the file is there. (audience member asking question without microphone) So, I think part of the answer here is that the standard does not really have a notion of different versions of the standard. There's just sort of the latest standard, and I'm not certain whether a modern compiler with a std C++11 flag would make this feature available in that mode. Because it's not part of C++11. So you might just get a compiler error because it doesn't know what has include is. Alright. So that was the last of the language features. We've got 10 minutes until break. I think we'll wait until, we'll start on library features after break if anybody has any questions. Yep. - [Audience Member] So the has include is not checking if this file has already been included in this compile time? - No it is -- - [Audience Member] But it could be included. - It is, yes. It is testing for whether you can include the header, yeah. Okay. Any other questions on this first half of the talk? Yep. (audience member asking question without microphone) The question was is there other forms of this to test for functions or symbols, or? Not right now, no. It's just this one particular one, has include, and you wouldn't be able to do that in the pre-processor anyways because that would involve parsing. Alright, I will see y'all after break.
Info
Channel: CppCon
Views: 21,235
Rating: undefined out of 5
Keywords: Bryce Adelstein Lelbach, CppCon 2017, Computer Science (Field), + C (Programming Language), Bash Films, conference video recording services, conference recording services, nationwide conference recording services, conference videography services, conference video recording, conference filming services, conference services, conference recording, conference live streaming, event videographers, capture presentation slides, record presentation slides, event video recording
Id: fI2xiUqqH3Q
Channel Id: undefined
Length: 48min 59sec (2939 seconds)
Published: Thu Nov 16 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.