# More and More Save

Contents[Show]

In the post Statically checked I wrote that the functions of the type-traits library are an ideal fit for static_assert. The reason is that static_assert requires a constant expression. The functions of the type-traits library provides a lot of checks which can be performed at compile time. With this posts I will prove my statement.

## gcd - The first

Before I describe systematically the functionality of the type-traits library I will start in this post with an example. My starting point is the euclid algorithm to calculate the greatest common divisor of two numbers.

It's quite easy to implement the algorithm as function template and feed it with various arguments.

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28``` ```// gcd.cpp #include template T gcd(T a, T b){ if( b == 0 ){ return a; } else{ return gcd(b, a % b); } } int main(){ std::cout << std::endl; std::cout << "gcd(100,10)= " << gcd(100,10) << std::endl; std::cout << "gcd(100,33)= " << gcd(100,33) << std::endl; std::cout << "gcd(100,0)= " << gcd(100,0) << std::endl; std::cout << gcd(3.5,4.0)<< std::endl; std::cout << gcd("100","10") << std::endl; std::cout << gcd(100,10L) << gcd(100,10L) << std::endl; std::cout << std::endl; } ```

But the compilation of the program fails. The compiler tries in vain to instantiate the templates. The function template has two serious issues. First, it is too generic. So the function template accepts doubles (line 21) and C strings (line 22). But it makes no sense to determine the greatest common divisor of both data types. The modulo operation for the double and the C string values fails in line 9. But that's not the only issue. Second, gcd depend on one type parameter T. This shows the function template signature gcd(T a, T b)). a and b have to be of the same type T. There is no conversion for type parameters. Therefore, the instantiation of gcd with an int type and a long type (line 24) fails.

Thanks to the type-traits library the first issue is quickly solved. The second issue requires much more effort.

## gcd - The second

I ignore for simplicity reasons in the rest of the post that both arguments have to be positive numbers. But back to the first issue. The static_assert operator and the predicate std::is_integral<T>::value help me to check at compile time whether T is an integral type. A predicate always returns a boolean value.

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24``` ```// gcd_2.cpp #include #include template T gcd(T a, T b){ static_assert(std::is_integral::value, "T should be an integral type!"); if( b == 0 ){ return a; } else{ return gcd(b, a % b); } } int main(){ std::cout << std::endl; std::cout << gcd(3.5,4.0)<< std::endl; std::cout << gcd("100","10") << std::endl; std::cout << std::endl; } ```

Great. I have solved the first issue of the gcd algorithm. The compilation will not fail by accident because the modulo operator is not defined for a double value and a C string. The compilation fails because the assertion in line 8 will not hold true. The subtle difference is that I now get an exact error message and not a cryptic output of a failed template instantiation as in the first example. The rule is quite simple. A compilation has to fail and I should get an unambiguous error message.

But what's about the second issue. The gcd algorithm should accept arguments of different type.

## gcd - The third

That no big deal. But stop. What is the type of the result?

 ```1 2 3 4 5 6 7 8 9``` ```template ??? gcd(T1 a, T2 b){ static_assert(std::is_integral::value, "T1 should be an integral type!"); static_assert(std::is_integral::value, "T2 should be an integral type!"); if( b == 0 ){ return a; } else{ return gcd(b, a % b); } } ```

The three questions marks in line 2 shows the core of the issue. Should the first type or the second type be the return type of the algorithm? Or should the algorithm derive a new type from the two arguments? The type-traits library comes to my rescue. I will present two variations.

### The smaller type

A good choice for the return type is to use the smaller of the both types. Therefore, I need a ternary operator at compile time. Thanks to the type-traits library we have it. The ternary function std::conditional operates on types and not on values. That's due to the fact that we apply the function at compile time. So we have to feed std::conditional with the right constant expression and we are done. std::conditional<(sizeof(T1) < sizeof(T2)), T1, T2>::type will return at compile time T1 if T1 is smaller than T2; it will return T2 if T1 is not smaller than T1

Let's apply the logic.

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38``` ```// gcd_3_smaller.cpp #include #include #include template typename std::conditional <(sizeof(T1) < sizeof(T2)), T1, T2>::type gcd(T1 a, T2 b){ static_assert(std::is_integral::value, "T1 should be an integral type!"); static_assert(std::is_integral::value, "T2 should be an integral type!"); if( b == 0 ){ return a; } else{ return gcd(b, a % b); } } int main(){ std::cout << std::endl; std::cout << "gcd(100,10)= " << gcd(100,10) << std::endl; std::cout << "gcd(100,33)= " << gcd(100,33) << std::endl; std::cout << "gcd(100,0)= " << gcd(100,0) << std::endl; std::cout << std::endl; std::cout << "gcd(100,10LL)= " << gcd(100,10LL) << std::endl; std::conditional <(sizeof(100) < sizeof(10LL)), long long, long>::type uglyRes= gcd(100,10LL); auto res= gcd(100,10LL); auto res2= gcd(100LL,10L); std::cout << "typeid(gcd(100,10LL)).name(): " << typeid(res).name() << std::endl; std::cout << "typeid(gcd(100LL,10L)).name(): " << typeid(res2).name() << std::endl; std::cout << std::endl; } ```

The key line of the program is the line 8 with the return type of the gcd algorithm. Of course, the algorithm can also deal with templates arguments of the same type. You can observe it in line 21 to 24 and in the output of the program. But what's about line 27? I use the number 100 of type int and the number 10 of type long long int. The result for the greatest common divisor is 10. The line 29 is extremely ugly. I have to repeat the expression std::conditional <(sizeof(100) < sizeof(10LL)), long long, long>::type to determine the right type of the variable uglyRes. Automatic type deduction with auto comes to my rescue (line 30 and 31). The typeid operator in line 33 and 34 shows that the result type of the arguments of type int and long long int is int; that the result type of the types long long int and long int is long int. ### The common type

Now to my second variation. Often it is not necessary to determine the smaller type at compile time but to determine that type to which all types can implicitly be converted to. That's the job of the function template std::common_type from the - of course you know it already - type-traits library. std::common_type can handle an arbitrary number of template arguments. To say it more formally. std::common_type is a variadic template.

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27``` ```// gcd_3_common.cpp #include #include #include template typename std::common_type::type gcd(T1 a, T2 b){ static_assert(std::is_integral::value, "T1 should be an integral type!"); static_assert(std::is_integral::value, "T2 should be an integral type!"); if( b == 0 ){ return a; } else{ return gcd(b, a % b); } } int main(){ std::cout << std::endl; std::cout << "typeid(gcd(100,10)).name(): " << typeid(gcd(100,10)).name() << std::endl; std::cout << "typeid(gcd(100,10L)).name(): " << typeid(gcd(100,10L)).name() << std::endl; std::cout << "typeid(gcd(100,10LL)).name(): " << typeid(gcd(100,10LL)).name() << std::endl; std::cout << std::endl; } ```

The only difference to the last implementation is that std::common_type in line 8 determines the return type. I ignore in this example the results of the gcd because I'm more interested in the types of the results. With the argument types int and int int I get int; with the argument types int and long int long int; with int and long long int long long int. ## gcd - The fourth

But that's not all. std::enable_if from the type-traits library provides in addition a very interesting variation. The previous implementations have in common that they will check in the function body If the arguments are integral types. The key observation is that the compiler always tries to instantiate the function temples and fails sometimes. You know the result. If the expression std::integral returns falls, the instantiation will fail. That is not the best way. It would be better if the function template is only available for the valid types. Therefore, I put the check of the function template from the template body to the template signature.

I order to concentrating on the essentials I used the same type for the function arguments. Therefore, the return type is obvious.

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24``` ```// gcd_4.cpp #include #include template::value,T>::type= 0> T gcd(T a, T b){ if( b == 0 ){ return a; } else{ return gcd(b, a % b); } } int main(){ std::cout << std::endl; std::cout << "gcd(100,10)= " << gcd(100,10) << std::endl; std::cout << "gcd(3.5,4)= " << gcd(3.5,4.0) << std::endl; std::cout << std::endl; } ```

Line 7 is the key line of the new program. The expression std::is_integral determines whether the type parameter T is integral. If T is not integral and therefore the return value false, I will not get a template instantiation. This is the decisive observation.

If std::enable_if returns true as first parameter, std::enable_if will have a public member typedef type. This type is used in the line 7. If std::enable_if returns false as first parameter, std::enable_if will have no public member typedef type. Therefore the line 7 in not valid. But this is not an error. Only the template for exactly this type will not be instantiated.

The rule in C++ says: When substituting the deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error. There is a shorter acronym for this rule SFINAE (Substitution Failure Is Not An Error).

The output of the compilation shows it. There is no template specialization for the type double. ## What's next?

The next post about the type-traits library will be systematically. The type-traits library has a lot of functions. They enables you to check, compare, and modify types at compile time. I will answer the two questions in the next post. How does it work? And. Which functions are available?

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Wolfgang Gärtner,  Louis St-Amour, Stephan Roslen, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Avi Kohn, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Tobi Heideman, Daniel Hufschläger, Red Trip, Alexander Schwarz, and Tornike Porchxidze.

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, and Richard Sargeant.

## Seminars

I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.

### Standard Seminars

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

### Modernes C++, #### Comments

0 #1 Maran 2016-10-17 10:42
Simple yet concrete examples. Thanks for the post.
0 #2 Flynn 2017-07-26 10:46
Já fiz varias tentativas porém nao consegui.
0 #3 giairuou.net 2017-12-04 12:41
Hello, I check your blog regularly. Your story-telling style is awesome, keep up the good work!

### Subscribe to the newsletter (+ pdf bundle)

 Name Email Please enable the javascript to submit this form

### Visitors

Today 4508

Yesterday 7909

Week 12417

Month 87468

All 6108532

Currently are 215 guests and no members online

### Latest comments

• #### std::span in C++20: Bounds-Safe Views for Sequences of Objects

I should clarify. I mean that std::span automatically deduces the size of a contiguous sequence ...

• #### std::span in C++20: Bounds-Safe Views for Sequences of Objects

Thanks for the write-up. Question/comment: as far as I understand span is not bounds-safe.

• #### C++ Core Guideline: The Guideline Support Library

Hi, Could this library be used in other platforms, especially embedded systems platforms? Thanks

• #### Executing a Future in a Separate Thread with Coroutines

What is the rationale for starting a new thread only to wait until this new thread is finished? It ...

• #### More Powerful Lambdas with C++20

Unfortunately, C++ lambdas are of limited value. In fact, anything that requires the auto keyword ...