Dec 15, 2025

[C++] [CppCon 2025] Implement the C++ Standard Library minute

Reference:

Implement the C++ Standard Library: Design, Optimisations, Testing while Implementing Libc++
[C++] Object Lifetimes reading minute

1)

1) tail padding.
2) if type is empty, it does not acquire address.

e.g.
struct Foo {
  int a;
  char c;
  bool b;
};

enum class ErrCode : int {
 E1, E2, E3,
};

template*<typename Val, Err>
struct Expected {
  union U {
  	[[no_unique_address]] Val val_;
	[[no_unique_address]] Err err_;
  };
  [[no_unique_address]] U value_;
  bool has_value_; 
};
Without `no_unique_address`, no tail padding,
sizeof(Expected<Foo, ErrCode>) == 12

With `no_unique_address`, 
bool has_value_;
can sneak into
U if U is of type Foo, `has_value_` would occupy Foo's tail padding,
thus:
sizeof(Expected<Foo, ErrCode>) == 8


2)

template<typename Val, Err>
struct Expected {
  union U {
  	[[no_unique_address]] Val val_;
	[[no_unique_address]] Err err_;
  };
  [[no_unique_address]] U value_;
  bool has_value_; 
  
  Expected(const Expected& other) :
  	has_value{other.has_value} {
	 if (has_value_) {
	 	std::construct_at(std::addressof(value_.val_), other.value_.val_);
	 } else {
	 	std::construct_at(std::addressof(value_.err_), other.value_.err_);
	 }
	}
};

Expected e1 = {Foo{}};
Expected e2 = e1;
assert(e2.has_value_); // Failed.
Why failed? Due to std::construct_at construct the value_ union's val_ and
zero out the padding, which has_value_ resides in due to `no_unique_address`.

Take away:

Don't mix no_unique_address with manual lifetime management
(union, construct_at, placement-new, std::bit_cast, std::start_lifetime_as, std::start_lifetime_as_array).


3)

Reference:
Segmented Iterators and Hierarchical Algorithms - Matthew H. Austern
https://lafstern.org/matt/segmented.pdf
Which is faster?
std::deque<int> d = ...

// 1
for (int& i : d) {
    i = std::clamp(i, 200, 500);
}

// 2 Faster due to using Segmented Iterators
std::ranges::for_each(d, [](int& i) {
    i = std::clamp(i, 200, 500);
});

Segmented Iterators:

template <class T> struct seg_array_iter
{
    // Local Iterator: position within the current segment (e.g., a vector<T>::iterator)
    vector<T>::iterator cur;
    
    // Segment Iterator: position among the segments (e.g., a vector<vector<T>*>::iterator)
    vector<vector<T>*>::iterator node;

    T& operator*() const { return *cur; }

    seg_array_iter& operator++() {
        // Standard increment: move to the next element in the current segment
        if (++cur == (**node).end()) {
            // End of segment reached: move to the next segment
            ++node;
            // Set the local iterator (cur) to the beginning of the new segment
            // or 0 (a null pointer equivalent for iterators) if it's the end.
            cur = *node ? (**node).begin() : 0;
        }
        return *this;
    }
    // ... other iterator operations like operator==, operator!=, etc.
};

template <class Iter, class T>
inline void fill (Iter first, Iter last, const T& x)
{
    typedef segmented_iterator_traits<Iter> Traits;
    
    // Dispatch call: The compiler chooses the segmented or nonsegmented helper 
    // based on the type of Traits::is_segmented_iterator (true_type or false_type).
    fill(first, last, x,
         typename Traits::is_segmented_iterator()); 
}

template <class SegIter, class T>
void fill (SegIter first, SegIter last, const T& x, true_type)
{
    typedef segmented_iterator_traits<SegIter> traits;

    // Decompose the full segmented iterators into segment and local iterators
    typename traits::segment_iterator sfirst = traits::segment (first);
    typename traits::segment_iterator slast = traits::segment (last);

    if (sfirst == slast) {
        // Case 1: Start and end are in the same segment. Use one local fill.
        fill(traits::local (first), traits::local (last), x);
    } else {
        // Case 2: Fill the *rest* of the starting segment
        fill(traits::local (first), traits::end(sfirst), x);

        // Case 3: Loop through all *middle* segments, filling them completely
        for (++sfirst; sfirst != slast; ++sfirst)
            fill(traits::begin(sfirst), traits::end(sfirst), x);

        // Case 4: Fill the *start* of the ending segment (sfirst now equals slast)
        fill(traits::begin(sfirst), traits::local (last), x);
    }
} 

Take away:

  • Use the most precise API for what you're trying to achieve.
    • insert_range instead of insert in a loop. (O(n) vs. N * log(M) )
    • use sorted_unique if the inputs are already sorted.
  • Use library facilities (e.g. views::zip) to benefit from concept-based optimizations.


4)

compiler flag
-Xclang -verify used for static_asserts
e.g.
clang++ -Xclang -verify -fsyntax-only test_integer_only.cpp

#include <type_traits>

template <typename T>
void strict_int_check() {
    static_assert(std::is_integral<T>::value, "Type must be an integer!");
}

void test() {
    // This line is valid, so no comment needed.
    strict_int_check<int>();

    // We WANT this line to fail. 
    // Without -verify, this breaks the build.
    // With -verify, Clang checks if the error matches the regex in {{...}}.
    strict_int_check<double>(); // expected-error {{Type must be an integer!}}
}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.