CodeNebula
C++20stladvanced

std::signed_integral / std::unsigned_integral (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Concepts that refine `std::integral` to distinguish signed and unsigned integer types. `signed_integral` matches types where `std::is_signed_v` is true; `unsigned_integral` matches the complement. Prevents subtle bugs from signed/unsigned mixing.

Real-World Scenario

Implement a safe subtraction function that only operates on signed integers, avoiding undefined behavior from unsigned underflow.

Quick Snippet

snippet.cpp
#include <concepts>
#include <stdexcept>

template<std::signed_integral T>
T safe_subtract(T a, T b) {
    if (b > 0 && a < std::numeric_limits<T>::min() + b)
        throw std::overflow_error("underflow");
    return a - b;
}

template<std::unsigned_integral T>
T safe_add(T a, T b) {
    if (a > std::numeric_limits<T>::max() - b)
        throw std::overflow_error("overflow");
    return a + b;
}

Full Example

signed_unsigned_integral.cpp
// CodeNebula - C++20 Example: std::signed_integral and std::unsigned_integral
// Compile: g++ -std=c++20 -Wall -Wextra -o example signed_unsigned_integral.cpp

// std::signed_integral matches integral types that are signed (int, long, etc.).
// std::unsigned_integral matches unsigned integral types (unsigned int, size_t, etc.).

#include <concepts>
#include <iostream>

// Only accept signed integers
template <std::signed_integral T>
T absolute_value(T value) {
    return value < 0 ? -value : value;
}

// Only accept unsigned integers
template <std::unsigned_integral T>
T safe_subtract(T a, T b) {
    if (b > a) {
        std::cout << "  Clamped to zero (would underflow)\n";
        return 0;
    }
    return a - b;
}

// Distinguish signed vs unsigned at compile time
template <typename T>
    requires std::integral<T>
void classify(T value) {
    std::cout << "Value " << value << " is ";
    if constexpr (std::signed_integral<T>) {
        std::cout << "signed integral\n";
    } else if constexpr (std::unsigned_integral<T>) {
        std::cout << "unsigned integral\n";
    }
}

int main() {
    // absolute_value works with signed types
    std::cout << "abs(-42) = " << absolute_value(-42) << "\n";
    std::cout << "abs(17L) = " << absolute_value(17L) << "\n";
    // absolute_value(42u);  // Compile error: unsigned int not signed

    // safe_subtract works with unsigned types
    std::cout << "safe_subtract(10u, 3u) = " << safe_subtract(10u, 3u) << "\n";
    std::cout << "safe_subtract(3u, 10u) = ";
    auto r = safe_subtract(3u, 10u);
    std::cout << "  Result: " << r << "\n";

    // Classify various types
    std::cout << "\nClassification:\n";
    classify(42);
    classify(-7L);
    classify(100u);
    classify(static_cast<unsigned short>(5));

    // Concept checks
    std::cout << "\nConcept checks:\n";
    std::cout << "int signed: " << std::signed_integral<int> << "\n";
    std::cout << "unsigned int signed: " << std::signed_integral<unsigned int> << "\n";
    std::cout << "unsigned int unsigned: " << std::unsigned_integral<unsigned int> << "\n";
    std::cout << "bool unsigned: " << std::unsigned_integral<bool> << "\n";

    return 0;
}

// Expected output:
// abs(-42) = 42
// abs(17L) = 17
// safe_subtract(10u, 3u) = 7
// safe_subtract(3u, 10u) =   Clamped to zero (would underflow)
//   Result: 0
//
// Classification:
// Value 42 is signed integral
// Value -7 is signed integral
// Value 100 is unsigned integral
// Value 5 is unsigned integral
//
// Concept checks:
// int signed: 1
// unsigned int signed: 0
// unsigned int unsigned: 1
// bool unsigned: 1
Run command
g++ -std=c++20 -Wall -Wextra -o example signed_unsigned_integral.cpp