Cookies   I display ads to cover the expenses. See the privacy policy for more information. You can keep or reject the ads.

Video thumbnail
Hello folks. We're going to start again.
We've got quite a bit more to get through, hopefully.
So let's pick up where we left off: with how to partially specialize a function the right way.
Remember from last time: Function templates cannot be partially specialized. You can't do it.
So if we have something that we want to behave like a function,
which is to say it's a procedure for doing a thing from some inputs and getting some outputs,
and we need it to be partially specialized on the type of those inputs,
the right way to do it is to delegate the partial-specialization work off to some thing that CAN be partially specialized...
...such as a class template.
Use the right tool for the job. If you need partial specialization, use something that can be partially specialized, and don't use something that can't.
So, in this case, I have a single function template called `is_pointer`; and all it does is instantiate one of these two class templates.
I have a primary template up top, and I have a partial specialization of this class...'s a partial specialization, because it's got angle brackets after the name `is_pointer_impl`...
It's got angle brackets. That means it's a partial specialization.
And I just dispatch to one or the other of them, and it works.
And this is also how I could implement the `is_void` trait earlier, using a full specialization.
Now you know everything there is to know about all these kinds of things. Which is nice.
But there's still one more thing that I want to explain. Let's talk about translation units.
So here's a puzzle — which we're not actually going to puzzle over — but...
Suppose we had to write a function to reverse the characters of a possibly multibyte string, in place.
Only POSSIBLY multibyte. We also want versions that work for ASCII, UTF-8, you know, different character sets. Latin-1.
But we don't want to do it using the old terrible, you know, `mblen`, `mbtowc`, all these different functions with locales, which is how C did it.
We would like it to be compile-time and be able to parameterize it on the character set itself. And we're going to do that as a template parameter.
So how I would use it might look something like this:
I've got this function called `reverse`, and it takes as a template parameter — Oops, so it's a function TEMPLATE. I keep calling these things "functions" —
It takes a template parameter called `Charset`, and that could be one of these little tag-structs up here: `struct ascii` or `struct utf8`...
And depending on the value — or, depending on the type — that's passed in, I get one behavior or the other.
Now, `reverse` can be implemented basically like this...
This is, you know, reverse each individual character and then reverse the entire string.
That's a good interview question, by the way. Remember those for your whiteboards.
And it relies on `mblen`, which is a function that's going to return, you know, given where I am in the string, tell me how many bytes are in this next character.
For ASCII, that's always going to be 1. For UTF-8, it could be anywhere from 1 to 4, or maybe 5.
So we're going to need to define some full specializations of `mblen` here.
`mblen` always returns 1.
`mblen` returns something complicated that — oh, well... hmm. I don't want to define all this in my header file. This makes me sad.
I would like to move the definition of this template function — this full specialization — out into a .cpp file. Another "translation unit." That's a term of art there.
So I can do that. This is a definition just like any other function definition,
and I can make it a function declaration just like any other function declaration by just putting a semicolon there.
And then off in my .cpp file I can write out the actual implementation of it, as we see here.
So here I have my .cpp file, at the bottom, which has the definitions of my full specializations.
And then in my header file, I have the declarations of those two specializations.
And I have the definition of the primary template for `reverse`. Does that make sense?
This works fine. However, that's still a lot of code.
I don't want all THAT code. It wasn't too much, it was like that much — but still I don't want it in my header file.
I want to put my implementation code in my .cpp file if I can.
So how do I get rid of that? Well that brings us to this next little bit of syntax here.
So I can— oops, not quite yet. First I can say, "okay, well, I'll just specialize it."
"I'll specialize it fully for `reverse` and `reverse`..."
I'll tell it that I have full specializations of these, and I'll put the full specializations down in the .cpp file.
But now I've repeated the implementation twice. And I don't want to do that either.
So what do I do now?
Okay, well, now we get to a new piece of syntax here. "Explicit instantiation definition."
This is a special syntax that means "Please instantiate this template with the given template parameters, as if it were being used right here."
It doesn't tell you how to define it; it just says, "I would like this defined, please."
So it looks just like a full specialization, except that after the word `template`, where a full specialization has empty angle brackets,
(I told you those were important, because now they go away!)
`template` WITHOUT the empty angle brackets is a completely different thing which has no relation to an explicit specialization.
And so it has a completely different name that could not possibly be confused with it called "explicit instantiation."
And it looks like this. The word `template` with no angle brackets, and then you declare the thing —
— again, basically as if you were going to use it. Like, `class vector`, for example; `bool is_void_v`;
and that just means "instantiate the thing right here, please."
Now, if I don't want to instantiate it right here, and I would just like to instantiate its DECLARATION, and say, well, it's defined somewhere else, I can just stick the word `extern` in front of it.
That's what `extern` does, right? It sort of says, like, "Here's the declaration. The definition is somewhere else."
And then you'd better define it somewhere else.
All right, so that's explicit instantiation definition and explicit instantiation declaration.
So there's one interesting thing about explicit instantiation for class templates which is different from anything else.
Because the weird thing about classes in C++ — the neat thing about them in particular — is they can have members.
A function cannot have members. A variable cannot have members. But a class can have members.
And so when I explicitly instantiate the definition of a class, that counts as an explicit instantiation of all of that class's members as well.
So when I say I'm instantiating a class `Foo`,
I also get `Foo::f` and `Foo::g` in this translation unit as well.
So this is a way, if you have a whole bunch of very similar templates and you would like to explicitly instantiate them all in one go,
maybe stick them all as static members of some class, and then just instantiate that class. Boom.
If that's a case you ever run into yourself, y'know.
So we can explicitly instantiate without specializing. It looks like this.
So here we have a primary template — still in the .h file —
and we're saying `extern template`, no angle brackets; so that's an explicit instantiation declaration
that says I have explicitly instantiated this somewhere in my code.
Not here. Don't worry about it, you know, just use it. Don't try to define it yourself.
And in the .cpp file, that's where I say `template` — no angle brackets —
and that's an explicit instantiation definition that causes `reverse` and `reverse` to get instantiated there.
Don't mix and match though. These angle brackets are important.
If I stick the angle brackets on up here, that says I have somewhere — not here because I don't have a body here, but somewhere — I have an explicit specialization, a full specialization, of `reverse`.
But I don't actually make one in my .cpp file. In my .cpp file, I don't specialize it. I just instantiate the primary template, because I forgot the angle brackets there. And so this is not guaranteed to work.
Although it does work, in practice, for reasons of name-mangling. Here's the deal with name-mangling.
This is exactly the case we just looked at, right? Right.
So on the left— ah, these are two translation units — the two little papery things — two different modules, two different .cpp files that I'm compiling.
And in the one .cpp file I've said `template<> int a(int)` — and that is saying, somewhere in my code I have a full specialization of `a`.
Not here, but somewhere. And I try to call it.
Across in the right-hand one, I instantiate the primary template.
Now it happens that instantiating the primary template generates some code, and the code's mangled function name—
People, is name-mangling, like, vaguely familiar? Yeah, okay.
So it happens that the name of the thing that gets instantiated there just happens to mangle to the exact same thing as the thing that I'm calling. And so it will work.
But try not to do that.
And if you reverse it, and I put over here — this is an explicit instantiation declaration, on the left, without the angle brackets.
And on the right, I've got a full specialization that says, "well, here's the full specialization."
We now have two competing definitions.
Oops, I'm sorry, the thing on the left is not a declaration; it's a definition, because I didn't say the word `extern`.
So we actually have two competing definitions.
And they're probably not the same, because the one on the left is an instantiation of the `\{ ... \}` in the primary template on the left,
and the one on the right has its own little `\{ ... \}` that might have something completely different in it.
And so the answer that I get from my `main` here is going to depend on whether it was inlined from MY translation unit, or if it generated an out-of-line function call to the other one...
And it could depend on optimization level...
Or what order they happen to get linked in... which order the linker decided to throw away the duplicate definition.
So this is fragile even in practice and may change depending on things. So don't mix and match.
Now you know everything there is to know about explicit instantiation!
That actually concludes Part 1. Awesome. Do we have any questions from the audience while I go to Part 2?
If you're lucky, you'll get an answer from the speaker while I go to Part 2, which is the bigger... Yeah.
Can I do explicit instantiation of classes and extern them as well?
I believe that you can. Not 100% sure about that, but I think so, and I think that would do the obvious thing,
which is to say, "I have definitely explicitly instantiated this somewhere, but it's not here" — and it would say that about all of the members of the class.
How do you explicitly instantiate a static member function of a class template where the static member function is actually a static member function template?
And it depends whether that class template—
Are you making a template that can stamp out instantiations of the static—
Well, you've got two level of cookie-cutter there, right? and you can do each one individually – and this is what you will see sometimes —
where you have, like, `template template`, some stuff.
That's the syntax for if I have a class template which has a member which is a template,
And here I am making a cookie cutter to stamp out cookie cutters to stamp out the actual definition of the thing. Because I have a template for a template of a thing.
And I'm not actually going to cover that, which is too bad!
All right. Does anyone need a refresher on Part 1? All right.
So let's talk about some idioms that we can use with templates.
Let's say I wanted to specialize on some condition that was more complicated than that "just make a full specialization for void" example that I gave earlier.
So here I have an example inspired from real life, where I have a tree container and a vector container, and they both have iterators.
I have a `tree_iterator` that, when I'm going through a tree, I can only `++` it. I can't hop `n` nodes ahead in the tree without following the `next` pointers.
But with a vector, I can just do a random access iterator.
And I have a `vector` and `set` down at the bottom using them.
Actually, okay, hang on. What is this? I said `tree_iterator&`.
I thought that whenever I referred to a class template I always needed to put the angle brackets because otherwise it wouldn't be able to deduce what I meant!
Well. There's a special case.
The special case is if I'm actually inside the definition of that class template itself, then I can kind of deduce which `T` I mean. And the `T` I mean is `T`.
I'm inside the class definition, where I've got my template parameters, like `Element`, so `tree_iterator` implicitly means `tree_iterator`.
Because I happen to be inside the definition itself of `tree_iterator`.
This is known as the — I'm going to say it's an "injected class-name," and I'm probably pretty close to right.
So yes, the bare template-name can be used as a typename. In which case it's basically as if you put all the template parameters after in angle brackets.
This is fine. It helps cut down on repetition, especially in declaring things like the copy assignment operator and things like that. I recommend that you use it.
And especially that you know what it means when you see it.
All right. So where were we?
So we have a standard algorithm called `advance` that takes an iterator of some type and advances it by N positions.
And here's the implementation of it. It just calls `++` N times.
For random access iterators, we can actually do better. We could write something like this.
This is `advance` specialized for `vector_iterator`s.
It takes a `vector_iterator` of some element type, and in that case it's going to return `begin + n`.
However! No! Wrong!
That would be — that looks like it's trying to be a partial specialization, right?
It's got the same name as this primary template, but it's sort of more specialized? But it's not fully specialized.
Function templates can be fully specialized, but they can't be partially specialized. No no no. Don't.
Therefore, we shouldn't do it this way.
We could do it the other way, by implementing it as a class template.
But let's just do something like this.
Let's say for `tree_iterator`... I'm going to generalize a little bit. I'm going to say we're not going to specialize JUST for `vector_iterator`.
Because I might add some new kind of random access iterator later.
So I've got my `tree_iterator`, and I'm going to add a member to it — because I control all these different iterator types —
And I can just add a member that says `supports_plus`: False. And `supports_plus`: True.
And then I can overload. I can make a pair of function templates, which are not— they don't look anything like partial specializations, because, again, they can't be —
Because they take a different parameter type as the third parameter.
One of them takes, particularly, `false_type`; and the other one takes, particularly, `true_type`.
And I'm going to pass `Iter::supports_plus` to those...
Let me go back one slide.
Let me point out, for those of you who are already familiar with tag dispatch, that in this case for the sake of pedagogy `supports_plus` is not a member typedef!
It is actually a static member variable of type `false_type`.
Have people seen `true_type` and `false_type`? This is one bit of `` that's actually useful to know.
You might say, "well, isn't that just `bool`?" No, it's not bool. These are actually two different types, as different as `int` and `double`. They're very different types.
And they happen to be, for some reason, associated with the semantic values of True and False.
It's as if we pretended that `int` were True and `double` were False. They're just two completely different types that happen to be associated with the concepts of "True" and "False."
And they're useful for things like this, where we're trying to overload on these concepts — these compile-time concepts of "True" and "False."
And that's NOT using "Concepts" in the Bjarne sense.
Yes, I could, but that would make this slide a little bit more complicated.
Does everyone see how this slide works, before we go to the next one?
All right, in practice, yes, it will look more like this.
So up here, instead of declaring a static data member called `supports_plus`, I will instead make a member alias, a member typedef, named `supports_plus`.
`tree_iterator::supports_plus` is now an alias for `false_type`. And `vector_iterator::supports_plus` is now an alias for `true_type`.
And I do the same thing with `advance_impl` here.
And my `advance` calls this function `advance_impl` with `begin` and `n` — and then this little expression here.
`typename Iter::supports_plus\`.
So the curly braces are there because we're trying now to create an object of that type, right? Those are the curly braces that you would... it could also be parens... that would also work.
But why the word `typename`? How many people have been writing template code and you've had to stick the word `typename` somewhere?
All right.
How many people got confused by that? All right. Good. Well, so what's going on with this `typename`?
So C++'s grammar is not context-free.
Normally, "context-free grammar" is one of these CS-something terms that you don't really need to know.
But in order to parse a function definition you actually need to know something about the context in which that function is being defined.
Namely, you need to know things like, in this example, is `A` a function or a type?
If `A` is a function, then `A(x)` is a function call.
If it's a type then this is like `int x`, all right? If `A` is a type, I'm just declaring `x` to be of type `A`.
So I need to know something about the context: "What is `A`?" And so if the context ends up being dependent on a template parameter,
where I don't know what that parameter is yet — `T::A`, I don't know what that is.
That might be a function; it might be a type; it might be something else, right?
So, how can this possibly work?
How can I have this function here — a function template — and I instantiate it with `S1`, where `S1::A` is a function, then this should be a function call to `S1::A`.
And in this case, `foo`... Well, this should be a declaration of an `int`. How can we possibly make this work?
The answer is we can't. That's too much work.
So we're just going to assume that any name that depends on a template parameter refers to a non-type, non-template, plain old entity.
It's going to be, you know, a variable or a function, or, you know... but it's a noun.
Well, okay, a type is a noun too. But it's not going to be a type.
It's not going to be a type or a template or any weird thing. It's going to be a regular old — just the normalest thing you can imagine.
So in this example, `foo`, if we try to compile that, it's going to go instantiate it and it's going to say, "`S2::A` is some plain old thing that I'm going to call with `x`... Oh, but it's not a plain old thing. It's `int`. Well, that doesn't make sense."
It's going to give you an error. It's going to say, "That was parsed as a non-type. I assumed it was a non-type originally, but instantiation of `foo` yields a type." So, hard error.
And this is how we fix it. We stick the word typename in front; to let the compiler know when it parses that template,
"Hey, this thing coming up? It might look like a regular old plain thing. You're going to assume it's a plain old thing because it's dependent on a template parameter, but it's not."
"I'm telling you it's a typename. So please treat it as a typename." That means this is a definition. A variable definition.
And now if I try to compile THIS one with `S1`, it will say, "Hey! You told me `T::A` was a typename, and it's not!" So it'll be an error.
Similarly we have things to refer to a template. Let's say I had these three types, `S1`, `S2`, `S3`...
...and I try to do this. Does this work?
I'll give you a moment to digest this a little bit.
`S1::A` is an `int` with value 0.
0 less than 0, greater than `x`, discard the result. Okay. Sure. That works great.
`T::` — so, that's `S2::` — now, at this point, `A` is not a plain old thing. `A` is a template. So we have to tell it, `template A`.
`template A`, okay, this is an angle bracket!
And the `0` is substituting in for `N`, so `N` equals `0` in that instantiation; and then we're going to call it with `x`.
Okay, so that works.
And then in `S3`, we actually have a variable declaration. We're declaring variable `x` to be of type `S3::A`, with `N` equal to 0.
So that's where these `typename` and `template` keywords come into play.
So hopefully from that you can figure out where to use them in the future.
Questions about `template`, `typename`, dependent contexts?
So let's go back to our type dispatch example. We see now why we have the `typename` down there. Because if we didn't, it would assume, because `Iter` is a template parameter, `Iter::supports_plus` is probably a variable.
And that's why my original example, where it WAS a variable, worked out great.
As soon as I make it a typename instead, well, now I need to tell it explicitly when it parses this template, "Hey, this is a typename coming up."
Otherwise I get a syntax error.
Okay, so now you know everything there is to know about tag dispatch... except... what if we don't control `Iter`?
Because this entire thing up here depended on my being able to insert this `supports_plus` member into `tree_iterator` and `vector_iterator`. What if I wanted to work with plain old pointers?
Okay, we have no class off of which to hang our `supports_plus` member typedef. What do we do? We make one up!
So I'm going to make up a new class. I'm going to call it `iter_traits`, and it's going to be a class template.
The primary template is going to say that by default `supports_plus` is an alias for `false_type`,
and for `vector_iterator`, `supports_plus` is going to be an alias for `true_type`.
This is a partial specialization, which is totally fine because class templates are allowed to do partial specialization.
And I'm going to make another partial specialization for `T*`, where `supports_plus` is also `true_type`.
And then I can just use `typename iter_traits::supports_plus`.
If `Iter` happens to be one of those types for which we've partially specialized the `iter_traits` class template, `supports_plus` will be `true_type`.
I will make a new instance of type `true_type`. I will pass that as the third argument to `advance_impl`. Overload resolution will say, "Ah! You must want the one that takes the third argument of type `true_type`."
And it will do the fast random-access implementation.
If it's not one of those, it will instantiate the primary template; create an instance of type `false_type`; pass that to the one whose third argument is `false_type`... yeah.
STL best practices: You can also, if you want — you take this `iter_traits::supports_plus`, and the fact that we had to write `typename` in front of it — you can again get rid of that...
by creating an alias template, and stick the `typename` up in the alias template.
And then your code gets cleaner, because an alias template always refers to a type, so we don't have to stick `typename` in front of it anymore.
Yes. So in tag dispatch, we have one more nested function call in here. Is the assumption that we can just inline it away?
Yep, it sure is. Compilers are great at inlining.
Now you know everything there is to know about traits classes!
Let's talk about declarations and definitions.
We have three cases here. One is normal entities like functions and variables.
This is the case that we should all be real familiar with. It's the case that exists in C.
So I have here a function declaration; a variable declaration;...
In the other translation unit — right? two different translation units here, two different .cpp files — I have the definitions.
And when I try to use them from the left-hand translation unit,
Well, they're declared; so it compiles fine, and then the linker finds the definitions over in the right-hand side, and it links, and we're happy.
`inline` functions and variables work a little bit differently. You can define them in two places and it will work just fine.
And the way that works is that the linker will find out that, "Oh! you have two definitions of this," and where normally that would be a multiple definition error, the linker will instead just throw away all but one of them.
It'll just quietly throw them away, and take whichever one happened to come in first or whatever heuristic it wants to use.
So in this case, I've actually defined `a` in two places. And this works just fine.
And I'm actually using it in two places as well.
"What if they're different definitions?" Then you have undefined behavior. So don't do that. Yeah.
Now suppose I do something like this, where I have a declaration of the inline function over here, but I haven't told the compiler how to define it.
But I'm still trying to use it. And then over on the right-hand side I have something that does create a definition of it.
So if I manage to get this all the way through the compiler to the linker, — which I believe you can in all major implementations with at most, like, a warning —
it will actually work, because the linker is totally happy. It says, "Oh! you're using `a`? I've got a definition for `a`!" And it links. But this is officially ill-formed.
So avoid this.
However, the linker will kind of do the right thing.
However, this is ill-formed because "an inline function or variable shall be defined in every translation unit in which it is ODR-used outside of a discarded statement."
And in the left-hand translation unit we have an inline function or variable which is ODR-used in `main`, but it is not defined.
It's defined in a different translation unit in the same program, which is why the linker is totally happy and it probably works in practice, but, yeah, officially, don't do this.
And especially — I mean a good reason not to do this in practice, either — is,
Let's say that someone does some refactoring on the right-hand side and they remove `foobar()`,
or let's say they turn on optimizations on the right-hand side and `a` gets inlined. So there's no longer a definition.
So at this point, there is no definition of `a` on the right-hand side because we didn't need it. So, you know, we got rid of it at compile time.
And we go to link, and suddenly there is no more `a`. And now we have an error.
So it's always good to make sure when you use an inline function make sure that you also provide the definition of it.
Because, I mean, that's the whole point of `inline`, right? We want to inline the definition into the call site, so... it'd be nice to tell it what the definition is.
So templates work under very very similar rules. Here I'm using a particular specialization of `a`,
and let's see —
By the way, I've been color-coding these in a little way. I've been underlining where the the thing is needed and "bluing" where the thing is actually defined.
So here it's needed and defined in the same place. And over in `foobar` it's needed and defined in the same place. And it all works great.
Again, we have two definitions; and again, the linker does its magic thing that it does for `inline`, which is to throw away all but one of them.
By the way, the buzzwords to remember there are "linkonce section", if you're on GCC or Clang or a Unixy-type operating system; or for Windows, "COMDAT sections" is how it's actually implemented. "Throw away all but one of them."
Or we could do something like this.
On the left-hand side I'm trying to use `a`, but I haven't told you how to define it. So it's not going to get defined over there.
But it will get defined on the right-hand side, because I have explicitly instantiated it.
And so I'm using the explicit instantiation [on the left]... I'm defining the explicit instantiation [on the right]... This should work.
In this case...
I am using an implicit instantiation `a` on the right-hand side, so it will get defined over there. It's needed on the left-hand side. The linker will probably link them together, and it will just work.
However, we have the same issues here as we had with the `inline` case. What if someone refactors away `foobar`? Then my code breaks.
What if `a` actually got inlined, and then the compiler decided that it didn't need to generate an out-of-line definition of it? Then my code breaks.
So while this will work at `-O0`, if you start turning up the optimization level and suddenly it breaks, it may be because you were using a template instantiation in a place where you have a declaration for it but you haven't defined it.
And so the compiler just generates the call, and then eventually it breaks in the linker.
Here's the example here; `a` is unused, and then of course we have a linker error.
There are at least four ways template stuff can go wrong. We're going to talk about some of them now.
When is instantiation needed?
So a decent rule of thumb is, "Never instantiate anything you don't 100% have to."
This is a decent rule of thumb for understanding the compiler's point of view.
When the compiler sees something like this — I have a template here and it has a `static_assert` inside it that says `sizeof(T)==2`.
And I'm trying to instantiate this class with `int`.
Now, `sizeof(int)` is not 2. `sizeof(int)` is 4 for most people. I'm going to assume that it is, for these slides.
So I try to instantiate it — and it works! I can actually create a pointer to a `C` just fine. The static assert does not fire.
And the reason the static assert doesn't fire is that it doesn't need to be instantiated.
That's in the body of the class – that's in the definition of the class — and in order to get a pointer to it, I don't need to have a definition of the class.
I don't need to know its members. I don't need to know how big it is.
All I need to know is, how big is a pointer to it. And the answer is "how big is a pointer on your system."
And I just need to know that it is a class; which it is. So this is totally fine.
Now if I actually were to create something without the `*` there, now suddenly I need to know "how big is the class," and that means I need to know what are its members.
Well, first, a quick sidebar on `static_assert`...
How many people know `static_assert`, like, pretty well — inside and out? Cool. All right.
`static_assert` is a compile-time way of making your program ill-formed and requiring a diagnostic.
This is nice if you are a library writer, and your user is doing something stupid that you want to stop them from doing, you can just stick a `static_assert` in there to say "Hey, you're probably doing it wrong."
Or, it's a sort of unit-test at compile time.
So if you are particularly relying on some invariant, and you really want to check it — like, `sizeof(int)==4` —
Like, "I know my code's going to break if we go to a platform with two-byte ints. I'm just going to `static_assert` it right here..."
"...and that way my code will fail to compile because the program will be ill-formed, if that ever breaks."
`static_assert(false)` always makes the program ill-formed. If it's some constant expression evaluating to `false`, and it doesn't depend on any template parameter,
you'll have a real hard time getting that past the compiler because the compiler will just be going along — even parsing through a template, even if you don't instantiate it —
and it'll say, "You know what?—" uh, you know, in this example, `f` won't even compile. Doesn't matter if I instantiate it or not.
I'm static_asserting inside it that `sizeof(int)` is zero. Well, that can't possibly be true.
And so the compiler will actually reject that out of hand, and just say, "No. `f` will never compile. This is dumb. You should just delete all of this code."
And that may sometimes not be what you want.
If you run into this situation where you're trying to `static_assert(false)` somewhere and it's not letting you, because it knows that `false` is always false,
the protip there is, make it depend on a template parameter.
Something like this, where I say `static_assert(sizeof(T)` for this template parameter `==0)` —
well, that's still never really true (barring compiler extensions) because no type has size zero;
but, because it's dependent on `T`, the compiler will actually wait until it knows what `T` is, to substitute it in and find out.
So what I have here is a function `g` that, if you use it, it will static_assert; and if you don't use it, it won't.
And again by "use" I mean something that actually requires the definition to get instantiated.
Does that make sense, on `static_assert`? Because this is useful.
So here's an example where I'm instantiating the definition of the class,
and if you look closely you'll see that each of the lines here — `sdm`, `smf`, and `f` —
— has a definition that also shouldn't compile! Right?
In two of the cases, I'm static_asserting that `sizeof(int)` is 2, but of course it's template-dependent, so it's okay.
And in the other case, I'm trying to take `nullptr` and cast it to `T`, which is `int`, and that shouldn't compile either. You can't cast `nullptr` to `int`.
But I can still create an object of type `C`. Because here, I don't need to know the definitions of `sdm`, `smf`, and `f`. All I need to know is their declarations.
I need to know the definition of the class, and therefore I need to know the declarations of all its members, to find out which ones take up space and so on;
but I don't need to know what they're initialized to.
So again the compiler is applying its rule of thumb. Which is, "Never instantiate anything you don't absolutely 100% have to instantiate."
(I'm going to skip a few of these slides.)
Remember that template definitions behave basically like they have the `inline` keyword attached to them.
So in this case —
We covered this actually a little bit earlier? with the two sides, and which things you could get away with in practice and which things you can't?
In this case you can get away with `ff1` in practice — even though you shouldn't do it —
because `ff1` might be defined somewhere else. The compiler will often let you off with a warning, and as long as the linker is happy, you're happy.
In case `ff2`, we will actually try to instantiate the body of `ff2` — of this inline function — and we will find out that the `static_assert` fails.
In fact, we'll find that out very quickly.
So, when I try to do `ff2` — when I try to even define `ff2` — it won't work.
When I templatize these...
With `ff1`, that's totally fine, but it better be defined elsewhere.
With `ff2`, now I'm trying to call the thing so I better try to define its body...
...and I find out that the definition of its body contains `static_assert` of something which is false; and therefore "static_assert failed."
If I add an explicit instantiation definition... Remember explicit instantiation definitions?
We have `extern template` — that says, "I promise that someone somewhere else has explicitly instantiated this, so you don't have to."
The compiler will say, "Oh, I don't 100% have to instantiate this? Cool. I won't." And it will completely ignore the `static_assert` and it won't fire.
It probably fires in the other translation unit, if you did it right, but it doesn't fire in yours.
Or if I fully specialize `ff3` and I say, "Somewhere else — not here, but somewhere else — I have made an explicit full specialization of `ff3`..."
"So you don't have to instantiate the primary template. In fact PLEASE don't instantiate the primary template. I WANT the full specialization from elsewhere."
And it says, "Okay, I won't. I don't care what's in the primary template anymore." So this is also fine.
Variable templates work very similarly to function templates and class templates.
But watch out; I have found that Clang and GCC currently disagree as to what constitutes a definition of a variable template.
So in GCC and MSVC, when I say `template int vt;`, they say, "Oh. Yeah. That's just as if I wrote `int vt;`."
It's a definition; it's a global variable definition. It reserves space, and so on.
For Clang, for some reason, this is just a declaration. I don't really understand it. It's as if you put the `extern` keyword in front of it.
If you wanted it to be a definition, you would need to actually initialize it. You could say `=0`, or empty curly braces, or something. And then Clang would say, "Okay, this is now a definition for sure."
I feel like this is a bug in Clang, but I don't really know.
All right, now you know everything there is to know about implicit instantiation!
I know that went, like, super fast and was probably confusing, but we're going to have less confusing stuff coming. So let's all forget that THAT section happened.
So hopefully by now everyone's seen variadic templates, so I'm going to skip over the motivation...
...and let's talk about variadic template parameter deduction! Yeah! All right!
So template parameter deduction in a nutshell. We already covered all of it except for variadic templates, which I said we'd get to in Part 2. So here it is.
What happens in this case? Here I have a template. It's called `f`, and it has a certain number of parameters.
One of them is named `T`. And the others are named `Us...`.
And it has some parameters, collectively named (lowercase) `us...`, which are of type (capital) `Us...`.
And so they all contribute to deduction — right? The parameters `us...` contribute to the deduction of template parameters `Us...`.
So what am I telling it?
Well, I'm telling it explicitly, through the angle brackets, that the first template parameter — which would be `T` — is `char`.
Okay, I know that `T` is fixed as `char`. Then I'm going to deduce what `Us...` are.
So it looks like the first one of `Us...` is `int`, and the second one is `int`, and the third one is `int`, and then we're done.
Okay. Cool. That's the simple case.
Let's try this. Now I'm telling it: well, the first template parameter, `T`, that's `char`. `T` is fixed as `char`.
And now the SECOND template parameter... Now this is interesting. The second template parameter is the first of the `Us...`.
That's also char. But I'm not saying anything about the other `Us...`. Okay?
So then we go and we look at the arguments. Well, the first argument — that's the first one of the lowercase `us...` — that doesn't contribute to deduction because we know that that the type of that is `char`.
And then the next one, that's an `int`, so we deduce the second one of `Us...` is `int`.
And the third one is `int`, and then we're done. Okay. And we get that result. Does that make sense?
Okay, two hands. Yeah.
[AUDIENCE] So, on the first line, if `0` were in single quotes... [INAUDIBLE]
[ARTHUR] If the `0` was in single quotes... on which line?
If the `0` were in single quotes, my `Us...` would be `char, int, int`.
[AUDIENCE] But `T` is `char`. Yeah. `T` is `char`.
But the three arguments all contribute to `Us...`. `T` is actually not deducible here. If I don't specify `T`, it won't compile at all.
Also question? No? Okay.
The `Us...` do not all have to be the same type. Just as when I pass multiple ints to a function as function parameters, the ints don't all have to have the same value...
...Here when I have multiple types as template parameters, they don't all have to be the same type.
Yes, you can just keep going as long as you want there, in the angle brackets. Or outside them, for that matter.
So as far as explicitly specified template parameters are concerned,
the first pack expansion encountered in the template parameter list basically just soaks up all of the remaining explicitly specified template parameters
(which are the things in the angle brackets).
The type deduction step might wind up lengthening that first pack. But it will never shorten it.
Right, where the programmer said, like, "I know that the first element of `Us...` is `char`, and the second element is `int`, and the third element is `double`, but I don't care what happens after that—"
Well, the compiler might go on and say, "Oh, well, I can DEDUCE that the fourth one is `int` and the fifth one is... whatever." Yeah.
So let's look at some of these cases.
In this case we are specifying explicitly that `T` is `int` and `Us...` begins with `char, int,` something something something. And `V` — we don't know anything about `V`.
Now you might say, well, I can look at this and I can predict that `V` would be `int`, and then `Us` must just be the single element `char`.
But that's working right to left, and that's not what the compiler does, and that's not what the standard says the compiler should do.
The compiler is doing its job in this case.
It's saying, okay, `T` is `int`, `Us...` is `char`, `int`, maybe-some-other-stuff, and `V` can't be deduced at all.
And because `V` can't be deduced at all, this will refuse to compile. Because it doesn't know what `V` is.
In the second case, `Ts...` starts out with `int` and `char`,
and then, well, that's all we know about that from the template parameter list in the angle brackets.
So then we go and we look, and we say, "Ah, we have one function parameter, and it's a `double` — so `U` must be `double`."
And then we're done. We have completely deduced everything there is to say about this function, and so it works.
In this case, let's see, we start off with `int, char`... so `Ts...` starts off as `int, char`...
so the first two parameters don't contribute to type deduction at all.
So it's okay that the second one isn't a `char`. It's an `int`, but it will get converted. And then there's a `double`, so we add that onto the pack.
So `Ts...` is just `int, char, double`; and that also works.
This is actually a little bit interesting, right? because you might say, well, I said `h`, but I'm giving it three arguments. Why doesn't it tell me I just have too many arguments?
The answer is: well, that's just not how variadic type deduction works.
Questions about this slide?
Yeah, you would think so, but no.
"Why can't `V` be deduced?" Well, because the `Us...` is the first pack expansion that we encountered, and it soaks up all the rest.
So `Us...` is `char` and `int` and maybe some other stuff; and then there's nothing left for `V`, and it can't be deduced from the parameter list.
"Why does the second one work, given that the first one failed?"
Because in that case we CAN deduce `U` from the parameter list. The parameter list tells us that `U` must be `double`.
I'm not sure what you mean by "expanded out the types."
Then `Ts...` would be ``, and `U` would be derived.
And it would probably complain about the number of parameters that you passed — well, in that case it'd be fine.
But yeah, there's no way to specify— In the second case, in `g`, there's no way to tell it explicitly what `U` is.
`U` can only EVER be deduced from the parameter list. Because anything I throw inside those angle brackets is just going to keep getting added to `Ts`.
There's no way to specify `U` in the angle brackets. Anything I throw in there is going to get soaked up.
Does that answer the question?
I believe yes, but I'm going to move on to the next bit.
So as far as deduction is concerned... So we talked about explicitly specified template parameters. Now let's talk about the parameter deduction.
A parameter pack contributes to deduction only if it comes at the very end of the parameter list.
Otherwise, it does not contribute to deduction.
So in this case I have an `f`, and I'm not going to specify anything about the angle brackets. I'm just going to rely on deduction.
I'm going to deduce that the first parameter is `U`, so that's a `char`? `U` is a `char`.
And then the rest of the parameters — `int`, `int` — those must be the `Ts...`. So it deduces just fine.
In the second case, the `us...` — all of these parameters — don't contribute to deduction.
So... I have a `T` at the end, but that doesn't work either. Like, there's just nothing I can say at this point.
I'm like, well, some of these `('x', 1, 2)` things are probably some of the `us...`, but the `us...` aren't contributing to deduction. So I can't say anything about capital `Us` from this.
And so, yeah, I just don't know where to start with this one.
And with case `h`...
Here I am explicitly stating that `T` is `int` and that `Us...` starts with `int, int`.
Now here the `us...` don't contribute to deduction, but we know they start with `int, int`, so let's see, okay...
This one — well, it's a `char`, but we said it was `int`, so, it's `int`. And this one is `int`.
And then we've run out of the `Us...` that were explicitly specified. So this next thing must be a `T`.
So `T` must be `int`.
We have in fact explicitly... Oops, sorry, actually the `T` doesn't contribute to deduction either. We already explicitly specified that one too. So this just happens to work.
It's not a very satisfying answer when I say it like that.
So the trick here is that the parameter pack contributes to deduction only if it comes at the very end of the parameter list.
So only in `f` do we deduce anything about `Ts...`. In the other cases, we're not deducing anything about `Us...`.
"Can I have two parameter packs as template arguments?"... like this?
So the trick is, what does it mean to contribute to deduction? Again, GCC and Clang differ on this, and MSVC tends to agree with GCC.
In this case I'm saying explicitly that the first one of `Ts...` must be `int`, the second one must be `int`, and I'm not saying anything about the others.
However, `Ts...` do not contribute to deduction.
So Clang says, well, you said `Ts...` couldn't contribute to deduction. That's the very first thing in here. I don't know where to start." You get a compiler error. Hard error.
MSVC and GCC both say, "Oh, well, you TOLD me it was ``; and I'm not sure what comes later, but okay."
So this is an `int`, that's an `int`, and then this next thing must be `Us...`.
And so the first of `Us...` must be an `int`. And then I run out of parameters.
so okay, so `Us...` is `` — oops, that's a typo there for ``.
I think these were originally— that was a `3.0`. If that was a `3.0`, this would be ``.
So yeah. So, again, there's some confusion in the compiler world about what "contribute to deduction" really means.
Now you know everything there is to know about template type deduction — even for variadics! And I think that's the LAST "everything" you need to know about template type deduction.
"What would happen if we just said `f(1,2,3)`?"
In that case I believe that Clang gives you a compiler error, and MSVC and GCC say, "Well, you didn't tell me anything about `Ts...`. So I'm going to assume it's empty, and `Us...` is `int, int, int`."
And that doesn't make a whole lot of sense; but this code doesn't make a whole lot of sense. Don't do this.
All right, let's take a quick break with some patterns. How many minutes do I have left, by the way?
All right. Well, let's try to real quick do this.
So the CRTP is a useful pattern to know.
And in fact it's so useful that you can come to see me afterward because I want to talk more about C++17 than I want to talk about these things.
So I hope you all do too. I hope you're all excited for C++17.
Because here's some things that are going to be new in it.
One of them I already mentioned — inline variables.
In fact, Bjarne mentioned it. He said don't use them because they're global variables and they're terrible.
I think, if you're GOING to use global variables, make sure they're `inline`.
"Can you make `static inline` variables?" I believe that you can.
Static data members that are `inline`. Yes, definitely yes to that one.
And that's pretty much all I have to say about inline variables. Let's talk about `template`. This is a neat thing.
But it's not really that neat.
I haven't talked much about template non-type parameters.
But by now when you see in all of the ways that you can kind of match up and deduce things...
You can figure out how a non-type parameter would work, if such a thing existed. Which it does.
For example, here there's a very common one...
Whenever you use a `std::array`, right, you say `std::array`. That `10` — still in the angle brackets — is a non-type parameter.
So in that case `T` is explicitly specified as `int` and the `N` parameter is explicitly specified as 10.
You can also deduce template non-type parameters.
And you can see deduction when you take— there's a function called `template size(T (&arr)[N])` that takes a reference to an array of `N` elements,
and then you can actually deduce what the `N` is in those cases.
However, mostly when you have these template non-type parameters, you will see them mostly in metaprogramming contexts.
Like, you can create these index-sequences of integers, like that.
And you also very frequently end up with things like `integral_constant`.
Actually, `true_type` and `false_type` are special cases of `integral_constant` that happen to be associated with `true` and `false`.
So I can do things like this.
And `template` — when that starts coming and you start getting scared by that — know that that is basically just collapsing something like the above...
...into that.
So instead of now having two parameters,
one to say "what is the type of the thing that's coming next" and the other one to say "what is the thing",
we can now just have one parameter.
And we no longer really care about what its type is. We can get it with `decltype` if we want it.
But, as with generic lambdas, we just say `auto` and we just get the thing.
Also, remember when I said in Part 1 that class templates can't do deduction?
There's a thing coming in C++17 that Bjarne alluded to.
This is the slide from before. Remember, I said that we can't do deduction on class templates?
Coming in C++17: a way to do deduction on class templates!
At least for the constructors thereof.
And the way this is going to work is, basically, whenever we see something like this, where there's a bare template-name for a class template, and it's not followed by any angle brackets,
when we're constructing an object of this type, we're going to pretend that we have a "make" helper function. Like `make_pair` or `make_tuple`.
We're going to pretend that that exists, and that it has the exact same signature as the constructor of the primary template.
So in this case we have a `make_myvec` that takes a `T`.
And we're going to pretend that instead of calling the constructor of `myvec` (which we don't know yet, which we're trying to deduce),
we're going to pretend that we called `make_myvec` of those exact same arguments.
And then we're going to do overload resolution on that, and we're going to do template type deduction, and so on.
And we're going to deduce that — aha, if I called `make_myvec(1)`, then `T` there would be `int`. So I'm going to say `T` here is `int`.
And I'm just going to assume that you want a `myvec`.
And this happens to work really great for `pair` and `tuple`.
Or here's a more complicated example. If I have two different constructors for `myvec`, it still works!
I'm making a `myvec`. I resolve `make_myvec(1)`. In this case, overload resolution looks at these —
Now these are not partial. This is not a partial specialization. Functions can't be partially specialized.
We have two different functions in the overload set, but this one is more specialized than the other.
So this is a better match... Oops, sorry, nope, in this case `1` is not a pointer. So we do this one.
`make_myvec` with `T=int`. And we get a `myvec`. So it does work right.
Now, in this case, we have an address of an `int`.
So in this case, this one is more specialized, so we take this one with `T=int`
and we get `myvec`, using the pointer parameter.
So this is a case where we have deduced something for `T` that is not the type of the thing we passed to it.
We passed it `int*`. But because there's this extra constructor that takes a `T*`, we can deduce `T=int`.
Does this make sense? This is the first non-totally-obvious example of how this might work.
Um, the naming of this `make_myvec`? This is a completely fictitious function. It does not exist in your code.
The compiler is kind of pretending that this "make" function exists based on the fact these constructors exist,
and then it's also pretending that you called it. You don't actually call it.
`make_pair` and `make_tuple` — well, they will still exist, for backward compatibility, but you will not continue to make "make" helpers.
Yes, it does. It does interact with with "Almost Always `auto`" syntax, as well.
`auto x = myvec(1)` or `auto x = myvec(&i)` will also work.
Here's another example.
We now have a class template `myvec` where it has one overload that takes a `T` and one overload that takes a `double`.
So let's pretend that we had these two function templates, and we're calling it with a `double`.
Well, it turns out that we're trying to call `make_myvec(1.0)`.
But the second overload — this one, which takes a `double` — we'd love to call this, but we don't know what `T` is. `T` can't be deduced. So it doesn't participate.
So we go to this one, and we say, "Ah, now `T` is `double`. Okay, I know about that." And we make a `myvec`.
Or in this case where I have a partial specialization of a class template — in this case it's super unclear.
I think not only will compilers disagree once this is implemented, but I think the standard needs a little bit of love in this instance.
But in this case it sure looks like we would get a compiler error, because we're going to deduce that `T` is `int*`.
And we're going to see that there's a partial specialization for `int*`; and we're going to try to call that constructor; and it doesn't exist. And we're going to have issues.
So there's a little bit of work to be done on the specification yet.
This will also mean that there will be a difference between these two classes, where right now there is not.
The difference here is that in the first case I have a `myvec` that takes a `T`;
and in the second case I have a `myvec` that takes some typedef for `T` — some member typedef for `T`.
And if I were to actually construct the overload resolution here, and I would try to call the red one, it would work great: `T` would be `int`.
In this [second] case, I know that `myvec::iter::type` is `int`. What does that say about `T`? Well, I don't know!
Because I'm back in the same problem I had in C++14. Of, "Well, I know that some crazy member—" I know SOMETHING about `T`. I know maybe its size,
or I know that some member of this particular template is `int`... That doesn't tell me what `T` is specifically.
So in the blue case I wouldn't be able to use the new feature. In the red case I am able to use the new feature.
And if you want to guide deduction, to get it to do the right thing, there's a syntax for that.
It looks a little odd. It doesn't really look like a declaration, but that's also because it's NOT really a declaration.
Because these constructors exist already. You don't need to re-declare them.
But you need to, in some way, guide; to say, "when someone does something that looks like a `myvec(double)`, and they don't say anything about which `myvec` they want, they probably want `myvec`."
And you do that with this exact syntax — with the little arrow.
That's a "deduction guide."
And they participate, and the third one is clearly the best one, and so on, and so on.
You know all the crazy stuff coming in C++17.
And now we're actually done ahead of time with two minutes remaining or something.
If anyone has any questions...? I can also go back and talk about mixins.
"Why, when I have `foo(1,2,3)`, does it deduce `` rather than ``?
Because `` works, and we don't like adding multiple ampersands unless we need to.
In fact, we never like adding MULTIPLE ampersands. And we only add ONE if we really, really need to.
That was from Part 1. I don't know, were you here for the first part? Okay. Well, the same rule applies as in Part 1.
All right, well, that's it. Thank you all for coming!