CodeNebula
C++20stladvanced

Range Type Traits: range_value_t, range_reference_t, range_difference_t, range_size_t (C++20 Addition)

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

Explanation

Type aliases that extract associated types from ranges: `range_value_t` gives the element value type, `range_reference_t` the reference type when dereferencing, `range_difference_t` the distance type, and `range_size_t` the unsigned size type.

Real-World Scenario

Deduce the accumulator type in a generic reduce function based on the range's value type.

Quick Snippet

snippet.cpp
#include <ranges>
#include <numeric>

template<std::ranges::range R>
auto reduce(R&& r) -> std::ranges::range_value_t<R> {
    std::ranges::range_value_t<R> sum{};
    for (auto&& elem : r) sum += elem;
    return sum;
}

Full Example

ranges_traits.cpp
// CodeNebula - C++20 Example: Range Traits (range_value_t, range_reference_t, etc.)
// Compile: g++ -std=c++20 -Wall -Wextra -o example ranges_traits.cpp

#include <ranges>
#include <vector>
#include <string>
#include <type_traits>
#include <iostream>

// Range traits extract type information from ranges:
// range_value_t, range_reference_t, range_difference_t, range_size_t, etc.

// Generic function using range traits
template <std::ranges::input_range R>
void describe_range(const R& r) {
    using value_t = std::ranges::range_value_t<R>;
    using ref_t = std::ranges::range_reference_t<R>;
    using diff_t = std::ranges::range_difference_t<R>;

    std::cout << "  value_type is integral: " << std::is_integral_v<value_t> << "\n";
    std::cout << "  reference is lvalue ref: " << std::is_lvalue_reference_v<ref_t> << "\n";
    std::cout << "  difference_type is signed: " << std::is_signed_v<diff_t> << "\n";

    // Use the traits to create a matching accumulator
    value_t sum{};
    for (const auto& elem : r) {
        sum = sum + elem;
    }
    std::cout << "  sum: " << sum << "\n";
}

// Return a new vector with elements doubled, using range_value_t
template <std::ranges::input_range R>
auto doubled(const R& r) {
    using T = std::ranges::range_value_t<R>;
    std::vector<T> result;
    for (const auto& elem : r) {
        result.push_back(elem + elem);
    }
    return result;
}

int main() {
    // Verify traits for vector<int>
    static_assert(std::same_as<std::ranges::range_value_t<std::vector<int>>, int>);
    static_assert(std::same_as<std::ranges::range_reference_t<std::vector<int>>, int&>);

    // Verify traits for string
    static_assert(std::same_as<std::ranges::range_value_t<std::string>, char>);

    std::vector<int> ints = {1, 2, 3, 4, 5};
    std::cout << "vector<int>:\n";
    describe_range(ints);

    std::vector<double> dbls = {1.5, 2.5, 3.5};
    std::cout << "vector<double>:\n";
    describe_range(dbls);

    // doubled() uses range_value_t to deduce return element type
    auto d = doubled(ints);
    std::cout << "Doubled ints: ";
    for (int v : d) std::cout << v << " ";
    std::cout << "\n";

    // Traits work with views too
    auto view = ints | std::views::transform([](int x) { return x * 0.5; });
    static_assert(std::same_as<std::ranges::range_value_t<decltype(view)>, double>);
    std::cout << "Transform view value_type is double: true\n";
    return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -o example ranges_traits.cpp