The type-traits library empowers you to compare and modify types. All is done at compile time therefore, there is no performance penalty.
Comparing types
The type-traits library supports three kinds of comparisons:
- is_base_of<Base, Derived>
- is_convertible<From, To>
- is_same<T, U>
Thanks to its member value, each class template returns true or false and is the optimal fit for static_assert.
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
|
// compare.cpp
#include <cstdint>
#include <iostream>
#include <type_traits>
class Base{};
class Derived: public Base{};
int main(){
std::cout << std::boolalpha << std::endl;
std::cout << "std::is_base_of<Base,Derived>::value: " << std::is_base_of<Base,Derived>::value << std::endl;
std::cout << "std::is_base_of<Derived,Base>::value: " << std::is_base_of<Derived,Base>::value << std::endl;
std::cout << "std::is_base_of<Derived,Derived>::value: " << std::is_base_of<Derived,Derived>::value << std::endl;
// static_assert(std::is_base_of<Derived,Base>::value,"Derived is not base of Base");
std::cout << std::endl;
std::cout << "std::is_convertible<Base*,Derived*>::value: " << std::is_convertible<Base*,Derived*>::value << std::endl;
std::cout << "std::is_convertible<Derived*,Base*>::value: " << std::is_convertible<Derived*,Base*>::value << std::endl;
std::cout << "std::is_convertible<Derived*,Derived*>::value: " << std::is_convertible<Derived*,Derived*>::value << std::endl;
// static_assert(std::is_convertible<Base*,Derived*>::value,"Base* can not be converted to Derived*");
std::cout << std::endl;
std::cout << "std::is_same<int, int32_t>::value: " << std::is_same<int, int32_t>::value << std::endl;
std::cout << "std::is_same<int, int64_t>::value: " << std::is_same<int, int64_t>::value << std::endl;
std::cout << "std::is_same<long int, int64_t>::value: " << std::is_same<long int, int64_t>::value << std::endl;
// static_assert(std::is_same<int, int64_t>::value,"int is not the same type as int64_t");
std::cout << std::endl;
}
|
The output of the program should not surprise you.

If I use the static_assert in lines 18, 26, and 34, the assertion will fire at compile time.

Modernes C++ Mentoring
Be part of my mentoring programs:
Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.
Modifying types
Now I'm a little bit pedantic. However, the C++ standard speaks about the modification or transformation of types that are not accurate. At compile time, there is no state. Therefore, there is nothing to modify. You can only generate new types on request. The type-traits library is template metaprogramming in a very beautiful robe. Template metaprogramming is a purely functional language that is embedded in C++. Purely functional languages have no state. I said that I will continue to speak about modifying types in the rest of this post.
The type-traits library has many functions to modify types at compile time. Therefore, you can remove or add const or volatile properties from a type. But there is more: Remove the sign of a type or the dimension of an array; change the pointer or reference properties of y type.
Here is the overview:
// const-volatile modifications
template <class T> struct remove_const;
template <class T> struct remove_volatile;
template <class T> struct remove_cv;
template <class T> struct add_const;
template <class T> struct add_volatile;
template <class T> struct add_cv;
// reference modifications
template <class T> struct remove_reference;
template <class T> struct add_lvalue_reference;
template <class T> struct add_rvalue_reference;
// sign modifications
template <class T> struct make_signed;
template <class T> struct make_unsigned;
// array modifications
template <class T> struct remove_extent;
template <class T> struct remove_all_extents;
// pointer modifications
template <class T> struct remove_pointer;
template <class T> struct add_pointer;
To get from a reference int& at compile time the type int, you have to use the member type of the class template. In C++14, this becomes a lot easier. You have only to add _t to the function. That holds for all invocated functions of this section.
std::cout << std::is_same<int,std::remove_reference<int &>::type>::value << std::endl; // true
std::cout << std::is_same<int,std::remove_reference_t<int &>>::value << std::endl; // true
The key of the code snippet is that you can write with C++14 std::remove_reference<int &>:: type simply in the form std::remove_reference_t<int &>. Thanks to value, you get the result of the comparison std::is_same
For completeness, I will mention that I should write about the modifications std::conditional, std::common_type, and std::enable_if. But I don't want to repeat myself. I presented the three functions in the post Statically checked. To get the rest of the details, read the Miscellaneous section transformations on the page cppreference.com.
One question is still open.
How does the whole magic work?
Due to a little bit of template metaprogramming, I can easily implement the class templates is_same and remove_const. I use the namespace rgr to distinguish my implementation from the C++ implementation.
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
39
40
41
42
43
44
45
|
// removeConst.cpp
#include <iostream>
#include <string>
#include <type_traits>
namespace rgr{
template<class T, class U>
struct is_same : std::false_type {};
template<class T>
struct is_same<T, T> : std::true_type {};
template< class T >
struct remove_const{
typedef T type;
};
template< class T >
struct remove_const<const T> {
typedef T type;
};
}
int main(){
std::cout << std::boolalpha << std::endl;
std::cout << std::is_same<int,std::remove_const<const int>::type>::value << std::endl;
std::cout << rgr::is_same<int,rgr::remove_const<const int>::type>::value << std::endl;
typedef rgr::remove_const<double>::type myDouble;
std::cout << rgr::is_same<double,myDouble>::value << std::endl;
typedef rgr::remove_const<const std::string>::type myString;
std::cout << rgr::is_same<std::string,myString>::value << std::endl;
typedef rgr::remove_const<std::add_const<int>::type>::type myInt;
std::cout << rgr::is_same<int,myInt>::value << std::endl;
std::cout << std::endl;
}
|
I implemented is_same and remove_const in the namespace rgr. This corresponds to the type-traits library. For simplicity reasons, I use the static constants std::false_type and std::true_type (lines 10 and 13). I presented them in the post Check types. Thanks to the base class std::false_type, the class template has a member value, respectively, for std::true_type. The critical observation of the class template is_same is to distinguish the general template (lines 9 and 10) from the partially specialized template (lines 12 and 13. The compiler will use the partially specialized template if both template arguments have the same type. The partially specialized template has, in opposite to the general template, only one type parameter. My reasoning for the class template remove_const is similar. The general template returns via its member type precisely the same type; the partially specialized template returns the new type after removing the const property (line 22). The compiler will choose the partially specialized template if its template argument is const.
The rest is quickly explained. I use in lines 31 and 32 the functions of the type-traits library and my versions. I declare in line 34 a typedef mydouble, a type myString (line 37), and a type myInt. All types are non-constant.
Here is the output of the program.

What's next?
I intentionally ignored the import capability of the type-traits library. In the first step, the compiler can analyze your types at compile time and perform powerful optimizations in the second step. The result is you have a faster program. Due to the type-traits library, this is happening in various standard template library algorithms. As far as I know, all implementations of the STL use this technique. I will write in a further post about this automatic optimization. But in the next post, I present user-defined literals. This is my favorite feature in modern C++ if you write safety-critical software. User-defined literals empower you to calculate with units. The compiler takes care that you don't compare apples and pears.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.
My special thanks to Embarcadero 
My special thanks to PVS-Studio 
My special thanks to Tipi.build 
My special thanks to Take Up Code 
Seminars
I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Bookable (Online)
German
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ - The Core Language
- C++ - The Standard Library
- C++ - Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
New
- Clean Code with Modern C++
- C++20
Contact Me
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: This email address is being protected from spambots. You need JavaScript enabled to view it.
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++,

Comments
is_base_of
where you list the kinds of comparisons.
Thanks. I fixed it.
I needed a template to tell me if a type was in a given type pack. In C++20, which I am using (lucky me), it's easy:
template
using includesType = std::disjunction;
This explains why the standard hasn't added such a trait. Hope this helps anyone else needing such a useful trait.
A duly pedantic response to that: the C++ template system is a Turing-complete language, so there definitely is state compile-time.
What you really meant was: "at compile time all state changes are predetermined".
(Of course this changes nothing, so type "modification" is indeed not something real from the user's perspective.)
RSS feed for comments to this post