Template Meta-programming in C++
What it is not
It is not some magic wand that will tell your C++ expertise. Many of us don't feel the need to use meta-programming just because we don't generally write library code or generic code in a day-to-day routine.
Like think of it, when have you last written some generic entity/class/functionality that can operate on different data types. Maybe you have, but most of us don't.
So if you don't know it, don't let your peers bully you just because of that.
What it is
But if you know it, you can do all sorts of crazy things that feel kind of magic. Its like when you think
this function should not be called on this type, doesn't the compiler already know that and it can chose not to call this function
Yes the compiler knows the type, but its "us" that need to tell the compiler what to do. Like you know this function should only be called on pointer types, and for other types, it should be invisible. Yes you can do that.
It is also all done in the compilation step, there is no overhead on the runtime of the program if you use some complex template construct.
But yes I can bet one thing on that. IT LOOKS COOL!
Creating our own "trait"
Yes they are called "traits". The properties that a type holds is called a trait.
This is not some magic. Let us build our own trait named "is_pointer".
So "is_pointer" is nothing but just a struct, with a templated parameter "T", that holds a enum with a member "value" that is always "false".
If now we call
So whatever type we create a struct for, value == false. But here comes the interesting part, we will SPECIALIZE the struct for the pointer type. Like this
Now this is a specialized struct that will be only instantiated when you give it a pointer type. See the value == true for all the cases where T is a pointer.
So now if we do
The program successfully compiles and the assertion is true.
Was it really that difficult? I don't think so.
Template Meta-programming is nothing but use of some of the specialized structs that are meant for some specific types. In this case "pointer" types.
Another chapter "SFINAE"
No it is not a mis-spelled word. It's literally called "sfinae".
Substitution Failure Is Not An Error. SFINAE.
领英推荐
But what are we substituting? Where is it? And what is not error? Yes C++ standards committee is sometimes pretty bad towards naming things. So whenever you define a function, let's say you have some overload of a function named "print", that is supposed to print some data onto stdout. So when you call the function named print(), the compiler generates a what is known as a "candidate set" of functions that the print is supposed to be called on.
So when we have some templated function that is specialized for some of the types, the candidate set is created during the compile time. And when you call that function, the compiler tries to match which of the candidate functions correctly match with the parameters that you are calling, this is called SUBSTITUTION.
And when a candidate function is rejected due to some mismatched types, it is called a SUBSTITUTION FAILURE.
Let me tell you with the above example.
So now can you read it? Let me help you,
Remember we have only defined the "print" function for the integral types, for all other types this function is not a candidate function, lets see the error that we get.
It says "no matching function call to print(double).
But SFINAE right, failure is not an error, so why does this error comes?
So you need to have a default print() function that can be called with some other type, maybe you can ignore the implementation. So in that case this specialized version for the integral types will not be a candidate function but the other default one. So this is where substitution failure occurred but it does not give an error, but choses the default type.
It takes a while to get used to, but its really fun when you try to practise meta-programming writing different traits.
What else?
Well we haven't even touched the tip of the iceberg. There is a lot more to it than this blog post can do justice. But let me show you one use case for SFINAE just to do some interesting things.
There are two classes, one that has a print() function, and one that does not.
So what we want to do is
create a generic print() function, that can take any type "T" that has a "print()" member function in it
So we can leverage SFINAE here with some other traits to have this behaviour.
So here we can read it like
There is a lot more to meta-programming that I can cover here in a blog. Do reach me out for some discussions if you are interested. Feel free to comment and correct if you see anything incorrect in this post.
Happy Coding!