Conversation
The const iterator declares its block array pointer as `const_pointer const*`, while the block array is a `std::vector<pointer>`. With `std::allocator` those are `T const* const*` and `T* const*`, and a qualification conversion makes the code compile. With a fancy pointer, e.g. the `boost::interprocess::offset_ptr` that metall hands out, they are `offset_ptr<T const> const*` and `offset_ptr<T> const*`, two unrelated class types, so `begin() const`, `cbegin()`, `end() const`, `cend()` and the converting constructor from the mutable iterator all fail to compile. The const iterator now points into `pointer const*`, which is what `m_blocks.data() const` returns for every allocator. Constness of the element stays with the `reference` and `pointer` member types, so the public iterator interface does not change.
The boost interprocess test only used the mutable paths, so the const iterator of segmented_vector was never instantiated for an allocator whose pointer is a fancy pointer. It now iterates the const map, looks a key up through it and erases through a const_iterator.
|
Closing this: it was opened before our own review was finished. The finding and the fix stand, and we will reopen once we have reviewed it internally. Apologies for the noise. |
|
Hi, I went through this before you closed it, so here is what I found. Short version: the change is good and I want it in. I checked it on a 7950X with clang 22 and gcc 16, boost 1.90, C++17 and C++23. Reverting just the I also wanted to know whether that one line is the whole story. So I wrote a small Two notes, neither of them blocking:
One thing worth writing down somewhere: Reopen whenever you're done and I'll merge it. Generated by Claude Code |
segmented_vector's const iterator declares its block array pointer asconst_pointer const*, while the block array is astd::vector<pointer>.With
std::allocatorthose two areT const* const*andT* const*, and a qualification conversion makes the code compile. With a fancy pointer they are unrelated class types. For aboost::interprocessallocator, and for metall, which builds on the sameoffset_ptr, they areoffset_ptr<T const> const*againstoffset_ptr<T> const*, sobegin() const,cbegin(),end() const,cend()and the converting constructor from the mutable iterator all fail to compile. Every const path of asegmented_mapover such an allocator is therefore unusable:find() const,erase(const_iterator), iteration over a const map.The const iterator now points into
pointer const*, which is whatm_blocks.data() constreturns for every allocator. Constness of the element stays with thereferenceandpointermember types, so the public iterator interface does not change:*itis stillvalue_type const&andit->is stillconst_pointer.custom_container_boost.cpponly used the mutable paths, so it never saw this. It now iterates the const map, looks a key up through the const map and erases through aconst_iterator. That test does not compile without the header change.How this was checked
clang 21.1.8, C++23, x86_64 Linux.
custom_container_boost.cppcompiles with the patch and does not compile without it. Stock 5.0.1 giveserror: no matching constructor for initialization of 'const_iterator' (aka 'iter_t<true>')at lines 1246, 1249, 1256 and 1259, the four constbegin()/end()overloads, anderror: cannot initialize a member subobject of type 'conditional_t<true, [2 * ...]>' with an lvalue of type 'const conditional_t<false, [2 * ...]>'at line 976, the converting constructor.ankerl::unordered_dense::segmented_map<uint64_t, uint64_t, ..., metall::manager::allocator_type<std::pair<uint64_t, uint64_t>>>into a metall datastore, fills it with 200000 entries, erases a third of them through const iterators, then closes the datastore. A second process blocks one page at the old address of the map withMAP_FIXED_NOREPLACE, so metall has to map the segment somewhere else, and reads everything back: same size, same checksum, and inserting after the reopen works. Segment address 0x71340c069f60 in the first run, 0x72bd90a69f60 in the second.std::allocatorcase is unaffected, checked with the same program oversegmented_map<uint64_t, uint64_t>.(prepared with claude)