@@ -145,8 +145,7 @@ struct to_impl;
145145// / auto e12 = g.add_edge(v1, v2);
146146// / auto e20 = g.add_edge(v2, v0);
147147// /
148- // / // (4)!
149- // / std::cout << "Vertices: " << g.n_vertices() << '\n';
148+ // / std::cout << "Vertices: " << g.n_vertices() << '\n'; // (4)!
150149// / std::cout << "Edges: " << g.n_edges() << '\n';
151150// /
152151// / for (auto neighbor : g.neighbors(v0)) // (5)!
@@ -174,8 +173,8 @@ struct to_impl;
174173// / - **Performance**: Descriptor-returning methods incur a slight overhead if the graph utilizes rich properties, as the descriptor must fetch the property payload. If you only need topology, prefer the `_ids` variants.
175174// /
176175// / ### Descriptor Invalidation Behavior
177- // /
178176// / The graph maintains the following invalidation semantics:
177+ // /
179178// / - **Vertex addition**: Does not invalidate vertex IDs. However, property references stored in vertex descriptors may be invalidated.
180179// / - **Vertex removal**: Invalidates vertex descriptors, IDs, and property references. Subsequent vertex IDs may shift depending on the implementation.
181180// / - **Edge addition**: Does not invalidate vertex or edge IDs. However, property references stored in edge descriptors may be invalidated.
@@ -193,10 +192,17 @@ struct to_impl;
193192// / - @ref gl::clone "clone" : Create a deep copy of a graph.
194193// / - @ref gl::to "to" : Convert a graph to a different implementation.
195194// /
196- // / > [!IMPORTANT] Const Correctness
195+ // / > [!IMPORTANT] Copy Semantics
197196// / >
198197// / > `graph` supports move semantics but disables copy assignment to prevent accidental expensive copies.
199198// / > Use @ref gl::clone "clone" to explicitly copy a graph.
199+ // /
200+ // / > [!WARNING] Const Correctness & Properties (API Note)
201+ // / >
202+ // / > Currently, a `const` graph guarantees **structural immutability** (vertices and edges cannot be added or removed).
203+ // / > However, vertex and edge property maps are internally treated as `mutable`. This means that property payloads
204+ // / > can still be modified through a `const graph&`. Strict const-correct overloads for property access are planned
205+ // / > for a future release. Proceed with caution in multi-threaded contexts.
200206template <traits::c_instantiation_of<graph_traits> GraphTraits>
201207class graph final {
202208public:
@@ -292,10 +298,7 @@ class graph final {
292298 // / @brief Adds a new vertex with specific properties.
293299 // / @param properties The property payload for the new vertex.
294300 // / @return A descriptor for the newly created vertex.
295- // /
296- // / > [!IMPORTANT] ID Stability
297- // / >
298- // / > Adding vertices does **not** invalidate existing vertex IDs. **However**, property references stored in vertex descriptors may be invalidated.
301+ // / @copydetails add_vertex()
299302 vertex_type add_vertex_with (vertex_properties_type properties)
300303 requires(traits::c_non_empty_properties<vertex_properties_type>)
301304 {
@@ -308,10 +311,7 @@ class graph final {
308311
309312 // / @brief Adds a specified number of default-initialized vertices to the graph en masse.
310313 // / @param n The number of vertices to add.
311- // /
312- // / > [!IMPORTANT] ID Stability
313- // / >
314- // / > Adding vertices does **not** invalidate existing vertex IDs. **However**, property references stored in vertex descriptors may be invalidated.
314+ // / @copydetails add_vertex()
315315 void add_vertices (const size_type n) {
316316 this ->_impl .add_vertices (n);
317317 this ->_n_vertices += n;
@@ -322,10 +322,7 @@ class graph final {
322322
323323 // / @brief Adds multiple vertices based on a range of property payloads.
324324 // / @param properties_rng A range of properties to initialize the new vertices with.
325- // /
326- // / > [!IMPORTANT] ID Stability
327- // / >
328- // / > Adding vertices does **not** invalidate existing vertex IDs. **However**, property references stored in vertex descriptors may be invalidated.
325+ // / @copydetails add_vertex()
329326 void add_vertices_with (
330327 const traits::c_sized_range_of<vertex_properties_type> auto & properties_rng
331328 )
@@ -365,57 +362,35 @@ class graph final {
365362 // / @brief Removes a vertex using its descriptor.
366363 // / @param vertex The descriptor of the vertex to remove.
367364 // / @throws std::out_of_range If the vertex descriptor is invalid.
368- // /
369- // / > [!WARNING] Descriptor and ID Invalidation
370- // / >
371- // / > Removing a vertex invalidates:
372- // / > - All vertex descriptors and IDs for vertices with higher IDs (they shift down).
373- // / > - All edge descriptors and IDs for edges incident to this vertex.
374- // / > - All references to vertex and edge properties obtained from the property maps.
375- // / > - References to vertex properties obtained via `vertex_properties()`.
365+ // / @copydetails remove_vertex(const id_type)
376366 gl_attr_force_inline void remove_vertex (vertex_type vertex) {
377367 this ->remove_vertex (vertex.id ());
378368 }
379369
380370 // / @brief Removes a range of vertices using their IDs.
381371 // / @param vertex_id_rng A forward range containing the IDs of vertices to remove.
382372 // / @throws std::out_of_range If any vertex ID in the range is invalid.
383- // /
384- // / > [!WARNING] Descriptor and ID Invalidation
385- // / >
386- // / > Removing vertices invalidates:
387- // / > - All vertex descriptors and IDs for vertices with higher IDs (they shift down).
388- // / > - All edge descriptors and IDs for edges incident to removed vertices.
389- // / > - All references to vertex and edge properties obtained from the property maps.
390- void remove_vertices_from (const traits::c_forward_range_of<id_type> auto & vertex_id_rng) {
373+ // / @copydetails remove_vertex(const id_type)
374+ void remove_vertices (const traits::c_forward_range_of<id_type> auto & vertex_id_rng) {
375+ // TODO: optimize
391376 // sorts the ids in a descending order and removes duplicate ids
392377 std::set<id_type, std::greater<>> vertex_id_set (
393378 std::ranges::begin (vertex_id_rng), std::ranges::end (vertex_id_rng)
394379 );
380+ if (not vertex_id_set.empty ())
381+ this ->_verify_vertex_id (*vertex_id_set.begin ());
395382
396- // TODO: optimize
397383 for (auto vertex_id : vertex_id_set)
398384 this ->_remove_vertex_impl (vertex_id);
399385 }
400386
401387 // / @brief Removes a range of vertices using their descriptors.
402388 // / @param vertex_rng A sized range containing the descriptors of vertices to remove.
403389 // / @throws std::out_of_range If any vertex descriptor is invalid.
404- // /
405- // / > [!WARNING] Descriptor and ID Invalidation
406- // / >
407- // / > Removing vertices invalidates:
408- // / > - All vertex descriptors and IDs for vertices with higher IDs (they shift down).
409- // / > - All edge descriptors and IDs for edges incident to removed vertices.
410- // / > - All references to vertex and edge properties obtained from the property maps.
411- void remove_vertices_from (const traits::c_sized_range_of<vertex_type> auto & vertex_rng) {
412- // TODO: optimize
413- // sort the ids in a descending order and removes duplicate ids
414- std::set<vertex_type, std::greater<vertex_type>> vertex_set (
415- std::ranges::begin (vertex_rng), std::ranges::end (vertex_rng)
416- );
417- for (auto vertex : vertex_set)
418- this ->_remove_vertex_impl (vertex.id ());
390+ // / @copydetails remove_vertex(const id_type)
391+ void remove_vertices (const traits::c_sized_range_of<vertex_type> auto & vertex_rng) {
392+ auto id_view = vertex_rng | std::views::transform ([](const auto & v) { return v.id (); });
393+ this ->remove_vertices (id_view);
419394 }
420395
421396 // --- vertex getters ---
@@ -658,8 +633,8 @@ class graph final {
658633 // / \f$
659634 // / deg(v) =
660635 // / \begin{cases}
661- // / deg_{in}(v) + deg_{out}(v) & \text{if } G \text{ is directed} \\
662- /// 2 \cdot |L(v)| + |E(v) \setminus L(v)| & \text{if } G \text{ is undirected}
636+ // / deg_{in}(v) + deg_{out}(v) & \text{if } G \text{ is directed}
637+ // / \\ 2 \cdot |L(v)| + |E(v) \setminus L(v)| & \text{if } G \text{ is undirected}
663638 // / \end{cases}
664639 // / \f$
665640 // /
0 commit comments