TypeTraits

C++ Core Guidelines: Programming at Compile Time with the Type-Traits

My journey through programming at compile time began in the last posts with template metaprogramming. Today, I jumped from C++98 to C++11. This is a jump to the type-traits library which is template metaprogramming in a standardized way.

 TypeTraits

The type-traits library has been part of C++ since C++11. Its origin is in boost. The type traits support type checks, comparisons, and type modifications at compile time. The library has over 100 functions, but each C++ standard adds new ones.

I will not go into the details of the type-traits library because I have already written posts about it: type-traits library. On the contrary, I cannot skip the type-traits library to continue my introduction to programming at compile time. In the end, here is my short introduction to the type-traits library. For more information, I add links to my older posts.

First of all, what’s inside the type-traits library?

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    The Type-Traits Library

    The library consists of type checks, type comparisons, and type modifications. Let me start with the type checks.

    Type Checks

    Each type belongs precisely to one primary type category.

    Primary type categories

    Here are they:

    template <class T> struct is_void;
    template <class T> struct is_integral;
    template <class T> struct is_floating_point;
    template <class T> struct is_array;
    template <class T> struct is_pointer;
    template <class T> struct is_null_pointer;
    template <class T> struct is_member_object_pointer;
    template <class T> struct is_member_function_pointer;
    template <class T> struct is_enum;
    template <class T> struct is_union;
    template <class T> struct is_class;
    template <class T> struct is_function;
    template <class T> struct is_lvalue_reference;
    template <class T> struct is_rvalue_reference;



    The following program gives you each primary type category a type.

     

    //  primaryTypeCategories.cpp
    
    #include <iostream>
    #include <type_traits>
    
    struct A{
      int a;
      int f(int){return 2011;}
    };
    
    enum E{
      e= 1,
    };
    
    union U{
      int u;
    };
    
    
    int main(){
      
      std::cout <<  std::boolalpha <<  std::endl;
    
      std::cout << std::is_void<void>::value << std::endl;
      std::cout << std::is_integral<short>::value << std::endl;
      std::cout << std::is_floating_point<double>::value << std::endl;
      std::cout << std::is_array<int []>::value << std::endl;
      std::cout << std::is_pointer<int*>::value << std::endl;
      std::cout << std::is_null_pointer<std::nullptr_t>::value << std::endl;
      std::cout << std::is_member_object_pointer<int A::*>::value <<  std::endl;
      std::cout << std::is_member_function_pointer<int (A::*)(int)>::value << std::endl;
      std::cout << std::is_enum<E>::value << std::endl;
      std::cout << std::is_union<U>::value << std::endl;
      std::cout << std::is_class<std::string>::value << std::endl;
      std::cout << std::is_function<int * (double)>::value << std::endl;	
      std::cout << std::is_lvalue_reference<int&>::value << std::endl;
      std::cout << std::is_rvalue_reference<int&&>::value << std::endl;
      
      std::cout <<  std::endl;
    
    }
    

     

    Here is the output of the program:

    primaryTypeCategories

    If you want to know how this magic works, my post Check Types provides more information.

    Based on the primary type categories are the composite type categories.

    Composite Type Categories

    The following table shows the relation between the primary type categories and the composite type categories. 

     CompositeTypeCategories

    There are more type checks possible with the type traits.

        template <class T> struct is_const;
    template <class T> struct is_volatile;
    template <class T> struct is_trivial;
    template <class T> struct is_trivially_copyable;
    template <class T> struct is_standard_layout;
    template <class T> struct is_pod;
    template <class T> struct is_literal_type;
    template <class T> struct is_empty;
    template <class T> struct is_polymorphic;
    template <class T> struct is_abstract;
    template <class T> struct is_signed;
    template <class T> struct is_unsigned;
    template <class T, class... Args> struct is_constructible;
    template <class T> struct is_default_constructible;
    template <class T> struct is_copy_constructible;
    template <class T> struct is_move_constructible;
    template <class T, class U> struct is_assignable;
    template <class T> struct is_copy_assignable;
    template <class T> struct is_move_assignable;
    template <class T> struct is_destructible;
    template <class T, class... Args> struct is_trivially_constructible;
    template <class T> struct is_trivially_default_constructible;
    template <class T> struct is_trivially_copy_constructible;
    template <class T> struct is_trivially_move_constructible;
    template <class T, class U> struct is_trivially_assignable;
    template <class T> struct is_trivially_copy_assignable;
    template <class T> struct is_trivially_move_assignable;
    template <class T> struct is_trivially_destructible;
    template <class T, class... Args> struct is_nothrow_constructible;
    template <class T> struct is_nothrow_default_constructible;
    template <class T> struct is_nothrow_copy_constructible;
    template <class T> struct is_nothrow_move_constructible;
    template <class T, class U> struct is_nothrow_assignable;
    template <class T> struct is_nothrow_copy_assignable;
    template <class T> struct is_nothrow_move_assignable;
    template <class T> struct is_nothrow_destructible;
    template <class T> struct has_virtual_destructor;

     

    Many of the function templates like is_trivially_copyable have the name component trivially. That means that these methods are not implemented by you but by the compiler.  Requesting a method from the compiler with the keyword default is also trivial.

    Type Comparisons

    The type-traits library supports three kinds of comparisons:

    • is_base_of<Base, Derived>
    • is_convertible<From, To>
    • is_same<T, U>

    The following example uses all three functions

     

    // 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;
      
      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;
      
      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;
      
      std::cout << std::endl;
      
    }
    

     

    and has the expected outcome.

    compare

    Programming At Compile Time

    Okay, let’s step back and think about the functions of the type-traits library. Here are a few observations.

    • The functions from the type-traits library are metafunctions because they run at compile time. Metafunctions are class templates
    • The metafunctions’ arguments that go into the sharp brackets (<…>) are metadata. Metadata are (in this case) types.
    • The return value of the functions is the (::value). This is just an alias. Since C++17, there is a more straightforward form for getting the result: instead of std::is_void<void>::value, you type std::is_void_v<void>.

    I hope these three observations remind you of my last post. These are precisely the conventions I presented in my previous post to template metaprogramming: C++ Core Guidelines: Programming at Compile Time.

    What’s next?

    If a function from the type-traits library wants to return a type, not a value, you must ask for it with ::type.  My following post shows which type of modifications the type-traits library supports at compile time. Ultimately, the type-traits library has two goals: correctness and optimization.

     

     

     

     

    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, 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, 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, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, Philipp Lenk, Hobsbawm, and Charles-Jianye Chen.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    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++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *