@@ -85,26 +85,23 @@ class graph final {
8585 using vertex_properties_map_type = std::conditional_t <
8686 traits::c_empty_properties<vertex_properties_type>,
8787 empty_properties_map,
88- std::vector<std::unique_ptr< vertex_properties_type> >>;
88+ std::vector<vertex_properties_type>>;
8989
9090 using edge_type = typename traits_type::edge_type;
9191 using edge_properties_type = typename traits_type::edge_properties_type;
9292
9393 using edge_properties_map_type = std::conditional_t <
9494 traits::c_empty_properties<edge_properties_type>,
9595 empty_properties_map,
96- std::vector<std::unique_ptr< edge_properties_type> >>;
96+ std::vector<edge_properties_type>>;
9797
9898 graph& operator =(const graph&) = delete ;
9999
100100 graph () = default ;
101101
102102 explicit graph (const size_type n_vertices) : _n_vertices(n_vertices), _impl(n_vertices) {
103- if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
104- this ->_vertex_properties .reserve (n_vertices);
105- for (auto _ : this ->vertex_ids ())
106- this ->_vertex_properties .push_back (std::make_unique<vertex_properties_type>());
107- }
103+ if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
104+ this ->_vertex_properties .resize (n_vertices);
108105 }
109106
110107 graph (graph&&) noexcept = default ;
@@ -129,10 +126,7 @@ class graph final {
129126 const auto new_vertex_id = static_cast <id_type>(this ->_n_vertices ++);
130127
131128 if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
132- return vertex_descriptor{
133- new_vertex_id,
134- *this ->_vertex_properties .emplace_back (std::make_unique<vertex_properties_type>())
135- };
129+ return vertex_descriptor{new_vertex_id, this ->_vertex_properties .emplace_back ()};
136130 else
137131 return vertex_descriptor{new_vertex_id};
138132 }
@@ -143,22 +137,16 @@ class graph final {
143137 this ->_impl .add_vertex ();
144138 return vertex_descriptor{
145139 static_cast <id_type>(this ->_n_vertices ++),
146- *this ->_vertex_properties .emplace_back (
147- std::make_unique<vertex_properties_type>(std::move (properties))
148- )
140+ this ->_vertex_properties .emplace_back (std::move (properties))
149141 };
150142 }
151143
152144 void add_vertices (const size_type n) {
153145 this ->_impl .add_vertices (n);
154146 this ->_n_vertices += n;
155147
156- if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
157- const auto old_size = this ->_vertex_properties .size ();
158- this ->_vertex_properties .reserve (this ->_n_vertices );
159- for (auto _ = old_size; _ < this ->_n_vertices ; ++_)
160- this ->_vertex_properties .push_back (std::make_unique<vertex_properties_type>());
161- }
148+ if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
149+ this ->_vertex_properties .resize (this ->_n_vertices );
162150 }
163151
164152 void add_vertices_with (
@@ -171,12 +159,12 @@ class graph final {
171159 this ->_impl .add_vertices (n);
172160 this ->_n_vertices += n;
173161
174- if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
175- for ( auto & properties : properties_rng)
176- this ->_vertex_properties .emplace_back (
177- std::make_unique<vertex_properties_type>(properties)
178- );
179- }
162+ if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
163+ this -> _vertex_properties . insert (
164+ this ->_vertex_properties .end (),
165+ std::ranges::begin (properties_rng),
166+ std::ranges::end (properties_rng)
167+ );
180168 }
181169
182170 gl_attr_force_inline void remove_vertex (const id_type vertex_id) {
@@ -231,7 +219,7 @@ class graph final {
231219 [[nodiscard]] gl_attr_force_inline vertex_type vertex_unchecked (const id_type vertex_id
232220 ) const noexcept {
233221 if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
234- return vertex_descriptor{vertex_id, * this ->_vertex_properties [vertex_id]};
222+ return vertex_descriptor{vertex_id, this ->_vertex_properties [vertex_id]};
235223 else
236224 return vertex_descriptor{vertex_id};
237225 }
@@ -241,21 +229,8 @@ class graph final {
241229 return this ->vertex_unchecked (vertex_id);
242230 }
243231
244- [[nodiscard]] gl_attr_force_inline auto vertices () const noexcept
245- requires(traits::c_empty_properties<vertex_properties_type>)
246- {
247- return this ->vertex_ids ()
248- | std::views::transform ([](const id_type id) { return vertex_descriptor{id}; });
249- }
250-
251- [[nodiscard]] gl_attr_force_inline auto vertices () const noexcept
252- requires(traits::c_non_empty_properties<vertex_properties_type>)
253- {
254- return this ->_vertex_properties | std::views::enumerate
255- | std::views::transform ([](const auto & x) {
256- const auto & [id, ptr] = x;
257- return vertex_descriptor{static_cast <id_type>(id), *ptr};
258- });
232+ [[nodiscard]] gl_attr_force_inline auto vertices () const noexcept {
233+ return this ->vertex_ids () | std::views::transform (this ->_create_vertex_descriptor ());
259234 }
260235
261236 [[nodiscard]] gl_attr_force_inline auto vertex_ids () const noexcept {
@@ -264,7 +239,7 @@ class graph final {
264239
265240 [[nodiscard]] gl_attr_force_inline auto neighbors (const id_type vertex_id) const {
266241 return this ->neighbor_ids (vertex_id)
267- | std::views::transform ([ this ]( const id_type id) { return this -> vertex (id); } );
242+ | std::views::transform (this -> _create_vertex_descriptor () );
268243 }
269244
270245 [[nodiscard]] gl_attr_force_inline auto neighbors (vertex_type vertex) const {
@@ -282,7 +257,7 @@ class graph final {
282257
283258 [[nodiscard]] gl_attr_force_inline auto predecessors (const id_type vertex_id) const {
284259 return this ->predecessor_ids (vertex_id)
285- | std::views::transform ([ this ]( const id_type id) { return this -> vertex (id); } );
260+ | std::views::transform (this -> _create_vertex_descriptor () );
286261 }
287262
288263 [[nodiscard]] gl_attr_force_inline auto predecessors (vertex_type vertex) const {
@@ -300,7 +275,7 @@ class graph final {
300275
301276 [[nodiscard]] gl_attr_force_inline auto successors (const id_type vertex_id) const {
302277 return this ->successor_ids (vertex_id)
303- | std::views::transform ([ this ]( const id_type id) { return this -> vertex (id); } );
278+ | std::views::transform (this -> _create_vertex_descriptor () );
304279 }
305280
306281 [[nodiscard]] gl_attr_force_inline auto successors (vertex_type vertex) const {
@@ -321,13 +296,13 @@ class graph final {
321296 requires(traits::c_non_empty_properties<vertex_properties_type>)
322297 {
323298 this ->_verify_vertex_id (id);
324- return * this ->_vertex_properties [id];
299+ return this ->_vertex_properties [id];
325300 }
326301
327302 [[nodiscard]] gl_attr_force_inline auto vertex_properties_map () const noexcept
328303 requires(traits::c_non_empty_properties<vertex_properties_type>)
329304 {
330- return util::deref_view (this ->_vertex_properties );
305+ return std::views::all (this ->_vertex_properties );
331306 }
332307
333308 // --- degree getters ---
@@ -380,18 +355,16 @@ class graph final {
380355 const auto new_edge_id = static_cast <id_type>(this ->_n_edges ++);
381356 this ->_impl .add_edge (new_edge_id, source_id, target_id);
382357
383- if constexpr (traits::c_non_empty_properties<edge_properties_type>) {
384- const auto & p =
385- this ->_edge_properties .emplace_back (std::make_unique<edge_properties_type>());
386- return edge_type{new_edge_id, source_id, target_id, *p};
387- }
388- else {
358+ if constexpr (traits::c_non_empty_properties<edge_properties_type>)
359+ return edge_type{
360+ new_edge_id, source_id, target_id, this ->_edge_properties .emplace_back ()
361+ };
362+ else
389363 return edge_type{new_edge_id, source_id, target_id};
390- }
391364 }
392365
393366 edge_type add_edge_with (
394- const id_type source_id, const id_type target_id, const edge_properties_type& properties
367+ const id_type source_id, const id_type target_id, edge_properties_type properties
395368 )
396369 requires(traits::c_non_empty_properties<edge_properties_type>)
397370 {
@@ -401,9 +374,12 @@ class graph final {
401374 const auto new_edge_id = static_cast <id_type>(this ->_n_edges ++);
402375 this ->_impl .add_edge (new_edge_id, source_id, target_id);
403376
404- auto & p =
405- this ->_edge_properties .emplace_back (std::make_unique<edge_properties_type>(properties));
406- return edge_type{new_edge_id, source_id, target_id, *p};
377+ return edge_type{
378+ new_edge_id,
379+ source_id,
380+ target_id,
381+ this ->_edge_properties .emplace_back (std::move (properties))
382+ };
407383 }
408384
409385 // clang-format off
@@ -427,12 +403,8 @@ class graph final {
427403 const id_type source_id, const traits::c_sized_range_of<id_type> auto & target_id_rng
428404 ) {
429405 this ->_verify_vertex_id (source_id);
430-
431- for (auto target_id : target_id_rng) {
406+ for (auto target_id : target_id_rng)
432407 this ->_verify_vertex_id (target_id);
433- if constexpr (traits::c_non_empty_properties<edge_properties_type>)
434- this ->_edge_properties .emplace_back (std::make_unique<edge_properties_type>());
435- }
436408
437409 const auto prev_n_edges = this ->_n_edges ;
438410 this ->_n_edges += std::ranges::size (target_id_rng);
@@ -441,18 +413,17 @@ class graph final {
441413 source_id,
442414 target_id_rng
443415 );
416+
417+ if constexpr (traits::c_non_empty_properties<edge_properties_type>)
418+ this ->_edge_properties .resize (this ->_n_edges );
444419 }
445420
446421 void add_edges_from (
447422 vertex_type source, const traits::c_sized_range_of<vertex_type> auto & target_rng
448423 ) {
449424 this ->_verify_vertex_id (source.id ());
450-
451- for (auto target : target_rng) {
425+ for (auto target : target_rng)
452426 this ->_verify_vertex_id (target.id ());
453- if constexpr (traits::c_non_empty_properties<edge_properties_type>)
454- this ->_edge_properties .emplace_back (std::make_unique<edge_properties_type>());
455- }
456427
457428 const auto prev_n_edges = this ->_n_edges ;
458429 this ->_n_edges += std::ranges::size (target_rng);
@@ -461,6 +432,10 @@ class graph final {
461432 source.id (),
462433 target_rng | std::views::transform (&vertex_type::id)
463434 );
435+
436+
437+ if constexpr (traits::c_non_empty_properties<edge_properties_type>)
438+ this ->_edge_properties .resize (this ->_n_edges );
464439 }
465440
466441 void remove_edge (const edge_type& edge) {
@@ -579,13 +554,13 @@ class graph final {
579554 if (id >= this ->_n_edges )
580555 throw std::out_of_range (std::format (" Got invalid edge id [{}]" , id));
581556
582- return * this ->_edge_properties [id];
557+ return this ->_edge_properties [id];
583558 }
584559
585560 [[nodiscard]] gl_attr_force_inline auto edge_properties_map () const noexcept
586561 requires(traits::c_non_empty_properties<edge_properties_type>)
587562 {
588- return util::deref_view (this ->_edge_properties );
563+ return std::views::all (this ->_edge_properties );
589564 }
590565
591566 // --- adjacency and incidence methods ---
@@ -627,19 +602,15 @@ class graph final {
627602 // --- comparison ---
628603
629604 [[nodiscard]] friend bool operator ==(const graph& lhs, const graph& rhs) noexcept {
630- constexpr auto val_eq = [](const auto & ptr_a, const auto & ptr_b) {
631- return *ptr_a == *ptr_b;
632- };
633-
634605 if (lhs._n_vertices != rhs._n_vertices or lhs._n_edges != rhs._n_edges )
635606 return false ;
636607
637608 if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
638- if (not std::ranges::equal ( lhs._vertex_properties , rhs._vertex_properties , val_eq) )
609+ if (lhs._vertex_properties != rhs._vertex_properties )
639610 return false ;
640611
641612 if constexpr (traits::c_non_empty_properties<edge_properties_type>)
642- if (not std::ranges::equal ( lhs._edge_properties , rhs._edge_properties , val_eq) )
613+ if (lhs._edge_properties != rhs._edge_properties )
643614 return false ;
644615
645616 return lhs._impl == rhs._impl ;
@@ -678,23 +649,11 @@ class graph final {
678649 using fmt_traits = io::detail::graph_fmt_traits<directional_tag>;
679650
680651 graph (const graph& other)
681- : _n_vertices{other._n_vertices }, _n_edges{other._n_edges }, _impl{other._impl } {
682- // Deep copy vertex properties
683- if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
684- this ->_vertex_properties .reserve (other._vertex_properties .size ());
685- for (const auto & property : other._vertex_properties )
686- this ->_vertex_properties .push_back (
687- std::make_unique<vertex_properties_type>(*property)
688- );
689- }
690-
691- // Deep copy edge properties
692- if constexpr (traits::c_non_empty_properties<edge_properties_type>) {
693- this ->_edge_properties .reserve (other._edge_properties .size ());
694- for (const auto & property : other._edge_properties )
695- this ->_edge_properties .push_back (std::make_unique<edge_properties_type>(*property));
696- }
697- }
652+ : _n_vertices{other._n_vertices },
653+ _n_edges{other._n_edges },
654+ _impl{other._impl },
655+ _vertex_properties{other._vertex_properties },
656+ _edge_properties{other._edge_properties } {}
698657
699658 // --- element validation ---
700659
@@ -731,6 +690,22 @@ class graph final {
731690 }
732691 }
733692
693+ // --- transformations ---
694+
695+ gl_attr_force_inline auto _create_vertex_descriptor () const noexcept
696+ requires(traits::c_empty_properties<vertex_properties_type>)
697+ {
698+ return [](const id_type id) { return vertex_type{id}; };
699+ }
700+
701+ gl_attr_force_inline auto _create_vertex_descriptor () const noexcept
702+ requires(traits::c_non_empty_properties<vertex_properties_type>)
703+ {
704+ return [&pmap = this ->_vertex_properties ](const id_type id) {
705+ return vertex_type{id, pmap[to_idx (id)]};
706+ };
707+ }
708+
734709 // --- I/O utility ---
735710
736711 struct concise_target_formatter {
@@ -898,13 +873,18 @@ class graph final {
898873 return is;
899874 }
900875
876+ // --- data members ---
877+
901878 size_type _n_vertices = 0uz;
902879 size_type _n_edges = 0uz;
903880
904- [[no_unique_address]] vertex_properties_map_type _vertex_properties{};
905- [[no_unique_address]] edge_properties_map_type _edge_properties{};
906-
907881 implementation_type _impl{};
882+
883+ // / @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier
884+ [[no_unique_address]] mutable vertex_properties_map_type _vertex_properties{};
885+
886+ // / @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier
887+ [[no_unique_address]] mutable edge_properties_map_type _edge_properties{};
908888};
909889
910890// --- general graph utility ---
0 commit comments