Open In App

<type_traits> Header in C++

Last Updated : 21 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

The <type_traits> header in C++ is a part of the metaprogramming library introduced in C++ 11. It is a collection of tools to help our code work with different data types. It contains Helper Classes which are standard classes that assist in creating compile-time constants, Type Traits, classes that obtain characteristics of types in the form of compile-time constant values, and Type Transformations which are classes that obtain new types by applying specific transformations to existing types.

<type_traits> in C++

<type_traits> contains templates that give information about type properties (like whether a type is integral or floating-point), relationships between types (like whether one type is derived from another), and modifications to types (like adding or removing constness). These inquiries can be used when writing generic algorithms or designing template libraries that need to adapt their behavior based on the types they operate on.

To use type traits in the program include the type_traits library.

#include <type_traits>

Syntax to Use Type Traits in C++

trait_name<T>::value

Here, 

  • trait_name is the name of the type trait you want to use.
  • T is the type you want to check.
  • The ::value part will give the result of the type trait for the given type T.

Categories of <type_traits> in C++

The type traits are classified into the following categories:

1. Unary Type Traits

The following functions are used to check the properties of a single type of object:

  • is_integral: is_integral checks if a type is an integral type.
  • is_floating_point: is_floating_point checks if a type is a floating-point type (e.g., float, double).
  • is_array: is_array checks if a type is an array.
  • is_const: is_const checks if a type is a const-qualified type.
  • is_reference: is_reference checks if a type is a reference type.

2. Binary Type Traits

The following functions are used to establish relationships between two objects:

  • is_same: is_same compares if two types are exactly the same.
  • is_convertible: is_convertible etermines if a value of one type can be converted to another.

3. Transformation Traits

The following functions are used to modify properties of a type:

  • add_const: add_const adds the const qualifier to a type.
  • add_volatile: add_volatile adds the volatile qualifier to a type.
  • make_unsigned: make_unsigned converts a signed type to its unsigned counterpart (if applicable).

Example to Use <type_traits> in C++

The below example demonstrates how we can use the <type_traits> for checking the type property.

C++
// C++ program to use <type_traits> for checking type
// properties

#include <iostream>
#include <type_traits>
using namespace std;

int main()
{
    // using is_integral template to check if a given type
    // is an integral type
    cout << "Is a given type an integral type? "
         << is_integral<int>::value << endl;

    // using is_floating_point template tp check if a given
    // type is a
    cout << "Is a given type floating-point type? "
         << is_floating_point<int>::value << endl;
}

Output
Is a given type an integral type? 1
Is a given type floating-point type? 0

Explanation: In the above example is_integral template checks if a type is an integral type (i.e., an integer type). If it is an integral type, it returns true (which is represented as 1), otherwise it returns false (0). Similarly, using is_floating_point template to check if a given type is a floating-point type or not.

Applications of <type_traits> in C++

Following are some common applications of <type_traits> header in C++:

1. Conditional Compilation

One of the primary uses of <type_traits> is conditional compilation based on type properties. By conditional compilation we can ensure only a certain type of data is processed during the compilation of our program.

Example:

In the below example demonstrates the use of is_integral to ensure both arguments are of numeric types before performing the operations.

C++
// C++ Program for conditional compiling

#include <iostream>
#include <type_traits>
using namespace std;

template <typename T1, typename T2>
// add integral check using type_traits

typename enable_if<is_integral<T1>::value
                       && is_integral<T2>::value,
                   decltype(T1() + T2())>::type
add(T1 a, T2 b)
{
    return a + b;
}

int main()
{
    // This will compile as both arguments are of intergral
    // type
    int sum1 = add(5, 3);
    cout << "Sum1 is: " << sum1;

    // This will not compile as both arguments are not of
    // floting point type
    int sum2 = add(11.5, 12.5);
    cout << "Sum2 is: " << sum1;
}


Output

Sum1 is: 8
error: no matching function for call to ‘add(double, double)’
19 |     int sum2 = add(11.5,12.5);

2. Compile-Time Type Inspections

<type_traits> are also used to check the type of a data during the compilation of the program.

Example:

In the below example demonstrates the use of is_pointer, is_array, is_const to ensure the data types matches the expected type of data by the functions.

C++
// C++ Program to check type of objects during compilation

#include <iostream>
#include <type_traits>
using namespace std;

// Function to check type properties
template <typename T>

void checkType(const T value)
{
    // Check if type is const
    if (is_const<T>::value) {
        cout << "Type is const." << endl;
    }
    else {
        cout << "Type is not const." << endl;
    }

    // Check if type is a pointer
    if (is_pointer<T>::value) {
        cout << "Type is a pointer." << endl;
    }
    else {
        cout << "Type is not a pointer." << endl;
    }

    // Check if type is an array
    if (is_array<T>::value) {
        cout << "Type is an array." << endl;
    }
    else {
        cout << "Type is not an array." << endl;
    }
}

int main()
{

    int x = 5;
    // Declaring a const type
    const int y = 10;
    // Declaring a pointer type
    int* ptr = &x;
    // Declaring an array type
    int arr[5];

    // Checking type properties for variable x
    cout << "Checking type for variable x:" << endl;
    checkType(x);
    cout << endl;

    // Checking type properties for const variable y
    cout << "Checking type for const variable y:" << endl;
    checkType(y);
    cout << endl;

    // Checking type properties for pointer variable ptr
    cout << "Checking type for pointer variable ptr:"
         << endl;
    checkType(ptr);
    cout << endl;

    // Checking type properties for array variable arr
    cout << "Checking type for array variable arr:" << endl;
    checkType(arr);
    cout << endl;

    return 0;
}


Output

Checking type for variable x:
Type is not const.
Type is not a pointer.
Type is not an array.

Checking type for const variable y:
Type is not const.
Type is not a pointer.
Type is not an array.

Checking type for pointer variable ptr:
Type is not const.
Type is a pointer.
Type is not an array.

Checking type for array variable arr:
Type is not const.
Type is a pointer.
Type is not an array.

3. Substitution Failure is Not an Error(SFINAE)

Substitution Failure Is Not An Error (SFINAE) is a C++ template metaprogramming concept that deals with the way the compiler handles substitution failures during template argument deduction. SFINAE is a key aspect of templates in C++ and is essential for writing generic, compile-time programmable code. The <type_traits> can be used to guide overload resolutions by using SFINAE. This allows the users to enable or disable function overloads based on the type properties.

Example:

C++
// C++ program for SFINAE

#include <iostream>
#include <type_traits>
using namespace std;

// Function template for processing integral values
template <typename T, typename enable_if<
                          is_integral<T>::value, int>::type
                      = 0>
void process(T value)
{
    cout << "Processing integral value: " << value << endl;
}

// Function template for processing floating-point values
template <typename T,
          typename enable_if<is_floating_point<T>::value,
                             int>::type
          = 0>
void process(T value)
{
    cout << "Processing floating-point value: " << value
         << endl;
}

int main()
{
    process(5);
    process(3.14);
    return 0;
}

Output
Processing integral value: 5
Processing floating-point value: 3.14

4. Compile Time Assertions

The <type_traits> header is also used for implementing compile time assertions in the program. This ensures that the code consists of only certain type of data allowed by the user or else it generates compile time errors. Function like is_arithmetic, is_array are used to restrict the type of arguments allowed by the user.

Example:

C++
// C++ Program for compile time assertions

#include <iostream>
#include <type_traits>
using namespace std;

template <typename T>
// Function allowing arithmetic type only
void printValue(const T& value)
{
    static_assert(is_arithmetic<T>::value,
                  "T must be an arithmetic type");
    cout << "Value: " << value << endl;
}

int main()
{
    // The following function calls will not cause an error
    // as both are of artihmertic types
    printValue(42);
    printValue(3.14);

    // this will generate a compile time error
    printValue("hello");
    return 0;
}


Output

error: static assertion failed: T must be an arithmetic type


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads