So have you ever wanted to build a function that does something based on the size of the type that it takes as a parameter? You might be tempted to do something like this (keeping things as generic as possible):
However attractive this might be, it seems rather inefficient. Especially if we combine it with other dynamic behavior, or things that the compiler would otherwise complain about (like type specific array allocators). I originally wanted this for a custom allocator. I realized there weren’t too many (if any) tutorials on how to make the compiler pick the right function using SFINAE.
The tools we need are std::enable_if and std::integral_constant. The key for std::enable_if that we need is the void type T and the specialization for “true” and none for “false” resulting in substitution failure given a boolean argument. If this isn’t immediately apparent, consider what would happen if you added a “false” specialization to the example given on en.cppreference.com as follows:
It’s pretty obvious a substitution failure wouldn’t happen and you would immediately have ambiguous functions (for which the compiler should complain). The next template we need is std::integral_constant which provides a nice base to build constant conditionals for our std::enable_if constructs.
Now on to what we actually want to do, build functions that are instantiated based on size. First we need to extend std::integral_constant for our usage. Here’s an implementation that restricts the size of the type for which true is returned to less than the given SIZE template parameter.
Conversely we can do the exact same thing for a range:
to be greater than LOWERBOUND and less than or equal to the UPPERBOUND. The next thing we need is a way to use this to return a type for SFINAE. To do that, we use std::enable_if combined with the typename keyword. This turns the naive if statement in the earlier function into a template for each case.
For the small size we can do something like this:
Then for a range a construct like this will work:
Admittedly the syntax for writing out the type for SFINAE enabling is quite long. if you want to shorten that, you can do something like this:
Which makes our invocation for the size range much shorter:
I realize this is a fairly contrived example, however it’s an example and extensions are many. My main usage of this is to provide size/type specific wrappers for allocators. This means I can choose placement, locality, page-structure, alignment, and packing based on exactly what type is being allocated at compile time. A fun extension is to populate the SIZE parameters at compile time based on architecture specific features (e.g., cache line size). Please post comments, suggestions, or feel free to point out errors. Enjoy!
As a user on reddit pointed out, you could easily include the sizeof invocation directly within the std::enable_if. This, however, isn’t a recommended programming practice so I chose not to go that route when composing this simple example. Consider using multiple sizes, or even changing the size range of a particular function multiple times; if you change the range, you now have N function templates to update. Not only do bugs crop up when updating, but this also makes for extremely ugly code once more conditionals are added to the std::enable_if construct. How would you like to maintain a header with twenty of these function templates, each with different logical conditions. Sounds like a nightmare to me. Packing these as separate std::integral_constant structs gives the programmer the ability to reason about them separately, change constant variables once and use lots, as well as write test cases for the std::integral_constant logic separate from the function logic.