type_traits


Header: <type_traits>

The <type_traits> header is a powerful component of the C++ Standard Library that provides a set of type traits. These are template classes and functions that give information about types at compile-time. This header is crucial for template metaprogramming, enabling compile-time decision making and optimization based on type properties.

Key Characteristics

Example 1: Basic Type Traits Usage

#include <iostream>
#include <type_traits>

template <typename T>
void printTypeInfo() {
    std::cout << "Is integral? " << std::is_integral<T>::value << std::endl;
    std::cout << "Is floating point? " << std::is_floating_point<T>::value << std::endl;
    std::cout << "Is pointer? " << std::is_pointer<T>::value << std::endl;
    std::cout << "Is const? " << std::is_const<T>::value << std::endl;
}

int main() {
    std::cout << "int:" << std::endl;
    printTypeInfo<int>();

    std::cout << "\ndouble:" << std::endl;
    printTypeInfo<double>();

    std::cout << "\nint*:" << std::endl;
    printTypeInfo<int*>();

    std::cout << "\nconst char:" << std::endl;
    printTypeInfo<const char>();

    return 0;
}

Explanation:

Example 2: Type Modifications

#include <iostream>
#include <type_traits>

template <typename T>
void printModifiedTypeInfo() {
    using RemoveConst = typename std::remove_const<T>::type;
    using AddPointer = typename std::add_pointer<T>::type;
    using RemovePointer = typename std::remove_pointer<T>::type;

    std::cout << "Original type is const? " << std::is_const<T>::value << std::endl;
    std::cout << "After remove_const is const? " << std::is_const<RemoveConst>::value << std::endl;
    std::cout << "After add_pointer is pointer? " << std::is_pointer<AddPointer>::value << std::endl;
    std::cout << "After remove_pointer is pointer? " << std::is_pointer<RemovePointer>::value << std::endl;
}

int main() {
    std::cout << "const int:" << std::endl;
    printModifiedTypeInfo<const int>();

    std::cout << "\nint*:" << std::endl;
    printModifiedTypeInfo<int*>();

    return 0;
}

Explanation:

Example 3: Conditional Compilation with enable_if

#include <iostream>
#include <type_traits>

// Function for integral types
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
isEven(T value) {
    return value % 2 == 0;
}

// Function for floating-point types
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
isEven(T value) {
    return static_cast<int>(value) % 2 == 0;
}

int main() {
    std::cout << "Is 4 even? " << isEven(4) << std::endl;
    std::cout << "Is 5 even? " << isEven(5) << std::endl;
    std::cout << "Is 3.14 even? " << isEven(3.14) << std::endl;
    std::cout << "Is 4.0 even? " << isEven(4.0) << std::endl;

    // This would cause a compilation error:
    // isEven("not a number");

    return 0;
}

Explanation:

Example 4: Type Relationships and Custom Traits

#include <iostream>
#include <type_traits>

class Base {};
class Derived : public Base {};
class Unrelated {};

// Custom type trait
template <typename T>
struct is_small_type : std::integral_constant<bool, sizeof(T) <= sizeof(void*)> {};

template <typename T>
void printRelationships() {
    std::cout << "Is same as Base? " << std::is_same<T, Base>::value << std::endl;
    std::cout << "Is base of Derived? " << std::is_base_of<T, Derived>::value << std::endl;
    std::cout << "Is derived from Base? " << std::is_base_of<Base, T>::value << std::endl;
    std::cout << "Is small type? " << is_small_type<T>::value << std::endl;
}

int main() {
    std::cout << "Base:" << std::endl;
    printRelationships<Base>();

    std::cout << "\nDerived:" << std::endl;
    printRelationships<Derived>();

    std::cout << "\nUnrelated:" << std::endl;
    printRelationships<Unrelated>();

    std::cout << "\nint:" << std::endl;
    printRelationships<int>();

    return 0;
}

Explanation:

Example 5: Compile-Time Assertions and Type Selection

#include <iostream>
#include <type_traits>

template <typename T>
class SafeIntegerContainer {
    static_assert(std::is_integral<T>::value, "SafeIntegerContainer can only be used with integral types");

public:
    void add(T value) {
        // Implementation...
    }
};

template <bool IsFloatingPoint, typename T>
struct NumberTraits {
    using AbsoluteType = typename std::conditional<IsFloatingPoint, 
                                                   long double, 
                                                   typename std::conditional<sizeof(T) <= sizeof(int), 
                                                                             int, 
                                                                             long long>::type>::type;
};

template <typename T>
typename NumberTraits<std::is_floating_point<T>::value, T>::AbsoluteType
getAbsoluteValue(T value) {
    return std::abs(value);
}

int main() {
    SafeIntegerContainer<int> intContainer;
    intContainer.add(5);

    // This would cause a compilation error:
    // SafeIntegerContainer<double> doubleContainer;

    std::cout << "Absolute value of -5: " << getAbsoluteValue(-5) << std::endl;
    std::cout << "Absolute value of -5.5: " << getAbsoluteValue(-5.5) << std::endl;

    return 0;
}

Explanation:

Additional Considerations

  1. Compiler Support: While most type traits are widely supported, some newer traits might not be available in older compilers.

  2. Performance: Type traits are resolved at compile-time and generally have no runtime cost.

  3. C++17 and Beyond: C++17 introduced _v suffixed aliases for type traits (e.g., std::is_integral_v<T>) for more concise usage.

  4. Custom Type Traits: You can create your own type traits following the patterns established in the standard library.

  5. SFINAE: Many type traits are useful in conjunction with SFINAE (Substitution Failure Is Not An Error) for template specialization.

Summary

The <type_traits> header in C++ provides a powerful set of tools for template metaprogramming and type manipulation:

These type traits are fundamental to modern C++ template metaprogramming, enabling developers to write more generic and efficient code. They allow for compile-time decision making, type-based optimizations, and safer template code.

By leveraging <type_traits>, C++ programmers can create more flexible and robust template libraries, implement compile-time optimizations, and write code that adapts to different types while maintaining type safety. This header is an essential tool for advanced C++ programming, particularly in template-heavy codebases and when writing generic libraries.

Related

Previous Page | Course Schedule | Course Content