JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
to.h File Reference

Container adaptation adaptor — constructs a target container C directly from a compatible range R. More...

#include <ranges>
#include "jh/metax/adl_apply.h"
#include "jh/conceptual/closable_container.h"

Go to the source code of this file.

Classes

struct  jh::ranges::to_fn< C >
 Function object implementing jh::ranges::to. More...

Namespaces

namespace  jh::ranges
 Semantic pipeline namespace for JH range operations.

Functions

template<typename C, std::ranges::range R, typename... Args>
constexpr auto jh::ranges::to_adaptor (R &&r, Args &&... args)
 Core implementation for jh::ranges::to adaptor.

Variables

template<typename C>
constexpr to_fn< C > jh::ranges::to {}
 Global instance of the to adaptor.

Detailed Description

Container adaptation adaptor — constructs a target container C directly from a compatible range R.

Author
JeongHan-Bae <mastropseudo@gmail.com>

The jh::ranges::to adaptor provides a high-efficiency way to directly construct a container C from a range R, as long as they form a valid closable_container_for pair.

Conceptually, this corresponds to the closable half of the proposed C++23 std::ranges::to. Unlike jh::ranges::collect, which can always materialize a range via insertion or emplace iteration, to requires that the container and range are structurally compatible and can be constructed directly.

Behavior overview

  • If C can be directly constructed from [begin(r), end(r)], the adaptor uses that constructor.
  • If C supports move iterators, vector bridging, or underlying container adaptation, these strategies are automatically detected and applied.
  • Constructor arguments (if any) are forwarded via Args....

Usage

using namespace jh::ranges;
std::vector<int> v = {1, 2, 3, 4};
std::pmr::monotonic_buffer_resource pool;
// Direct construction — requires closable_container_for<std::pmr::vector<int>, std::vector<int>>
auto pmr_vec = to<std::pmr::vector<int>>(
v,
std::pmr::polymorphic_allocator<int>(&pool)
);
// Equivalent pipe form
auto pmr_vec2 = v
| to<std::pmr::vector<int>>(std::pmr::polymorphic_allocator<int>(&pool));
// When the source range is not directly closable, normalize first:
auto pmr_vec3 = std::views::iota(0, 5)
| std::views::transform([](int x) { return x * x; })
| collect<std::vector<int>>()
| to<std::pmr::vector<int>>(std::pmr::polymorphic_allocator<int>(&pool));
Semantic pipeline namespace for JH range operations.
Definition sequence.h:156
constexpr to_fn< C > to
Global instance of the to adaptor.
Definition to.h:347
constexpr collect_fn< C > collect
Global instance of the collect adaptor.
Definition collect.h:375

Design rationale

  • Closability-first design: Only works if C and R form a valid closable_container_for relation.
  • Optimal constructor dispatch: Prefers native constructors, falls back to intermediate std::vector bridges if needed.
  • Full pipe compatibility: Works seamlessly in range pipelines with or without additional constructor parameters.

Optimization semantics: collect + to

Under optimized compilation (C++20 and later), the combination collect + to behaves as a semantic two-stage pipeline but is compiled down to a near-optimal one-stage construction.
Although collect materializes an intermediate container (usually std::vector<T>), the compiler's RVO and move-propagation rules ensure that no redundant copies occur for right-value ranges.

StageResponsibilityTypical BehaviorOptimization Result
collect<V>()Terminates lazy views and produces a normalized, value-semantic container (e.g. std::vector<pair<K,V>>).Constructs a temporary container on the caller's stack.RVO creates the container directly in the caller frame; no copy or move is required.
to<C>()Adapts the collected data into the final container C via its constructor ([begin, end), move-iterators, or adapter dispatch).Passes iterators of the temporary container.Because the source is a right-value, the target container moves each element; no deep copy occurs.
Overall pipelineTwo semantic stages: materialization + construction.Intermediate vector exists logically but not physically duplicated.Equivalent runtime cost to C(std::make_move_iterator(...)); no extra allocations, deterministic lifetime.

Why this design is semantically superior

  • Explicit materialization point: The exact moment a lazy range becomes concrete is visible and controllable.
  • Allocator and resource safety: Construction parameters are isolated in to, preventing accidental cross-allocator moves.
  • Predictable evaluation order: No hidden eager materialization as in std::ranges::to.
  • Copy-safe yet move-efficient: Even though return C(begin(r), end(r)) is specified as a copy operation, compilers perform per-element move for right-value containers, achieving optimal performance without losing semantic clarity.
  • Transparent lifetime management: The intermediate vector's scope is clear, making debugging and reasoning about resource ownership trivial.

In practice, collect + to achieves the same performance as a monolithic std::ranges::to call while providing stronger guarantees of safety, clarity, and composability within a range pipeline.

See also
jh::ranges::collect
jh::concepts::closable_container_for
Version
1.3.x
Date
2025