Date: Wed, 30 Jul 2025 02:55:38 +0300
Hello everyone!
Recently I've noticed that the std::from_range_t constructor of std::vector,
when given an rvalue container, does not move its elements into the std
::vector,
but instead copies the elements:
-
auto vector = std::vector { std::from_range, std::array { noisy {
}, noisy { } } };
Compiler Explorer: click <https://godbolt.org/z/6T3cWqxa3>
I am aware that I can use std::views::as_rvalue, but applying it manually
should not be necessary,
that is, the std::from_range_t constructor of std::vector should
automatically detect
that a container-compatible-range is an actual container, which has value
semantics and owns its elements,
and in that case move elements from that container into the std::vector.
In my opinion, vast majority of C++ programmers will except that such an
optimization will be performed
and unknowingly cause huge performance issue. We are talking about gigantic
performance degradation!
The issue is the P1206R7
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1206r7.pdf> and
the C++ Ranges Library do not define the std::ranges::container concept,
which would allow us to determine reliably from which ranges elements could
be safely moved out,
when that range is about to be destroyed.
Also, I see that for the purpose of specifying the std::ranges::to facility
in the C++23 standard,
there are two exposition-only concepts defined, which attempt to define
some sort of container concept,
but I don't think they are sufficient to detect whether a range is a
container or not:
- template< class Container >
constexpr bool reservable-container =
ranges::sized_range
<https://en.cppreference.com/w/cpp/ranges/sized_range.html><Container> &&
requires (Container& c, ranges::range_size_t
<https://en.cppreference.com/w/cpp/ranges/range_size_t.html><Container> n
)
{
c.reserve(n);
{ c.capacity() } -> std::same_as
<https://en.cppreference.com/w/cpp/concepts/same_as.html><decltype(n)>;
{ c.max_size() } -> std::same_as
<https://en.cppreference.com/w/cpp/concepts/same_as.html><decltype(n)>;
};
- template< class Container, class Reference >
constexpr bool container-appendable =
requires (Container& c, Reference&& ref)
{
requires
(
requires { c.emplace_back(std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; } ||
requires { c.push_back(std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; } ||
requires { c.emplace(c.end(), std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; } ||
requires { c.insert(c.end(), std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; }
);
};
Moreover, none of the existing concepts from the C++ Ranges Library are
suitable either:
- The std::ranges::view concept was redefined / relaxed multiple times,
but unfortunately the final version does not help us here,
since a range modelling the std::ranges::view concept
might not have reference semantics and might own its elements,
for example the std::ranges::owning_view and std::optional.
This is surprising and confusing in my opinion, considering that
views originally were supposed mean ranges referencing / pointing to
elements,
like std::span and std::string_view.
[image: ranges.png]
- The std::ranges::borrowed_range concept does not help us either,
because it's opt-in, that is, it does not detect automatically
whether a std::ranges::range owns its elements or just references them,
and does not decide based on that information whether a function
taking that range by-value can return an iterator obtained from that
range safely,
that is, without a danger of dangling.
Consequently, there might exist a range, which does not own its elements,
but does not opt-into the std::ranges::borrowed_range concept.
A good example is a custom span-like type written in C++17.
The ideas of ownership and value semantics are fundamental in C++ and Rust,
thus, in my opinion, the C++ Ranges Library should provide the std::ranges::
container concept.
What do you think?
Thank you, Mateusz Zych
Recently I've noticed that the std::from_range_t constructor of std::vector,
when given an rvalue container, does not move its elements into the std
::vector,
but instead copies the elements:
-
auto vector = std::vector { std::from_range, std::array { noisy {
}, noisy { } } };
Compiler Explorer: click <https://godbolt.org/z/6T3cWqxa3>
I am aware that I can use std::views::as_rvalue, but applying it manually
should not be necessary,
that is, the std::from_range_t constructor of std::vector should
automatically detect
that a container-compatible-range is an actual container, which has value
semantics and owns its elements,
and in that case move elements from that container into the std::vector.
In my opinion, vast majority of C++ programmers will except that such an
optimization will be performed
and unknowingly cause huge performance issue. We are talking about gigantic
performance degradation!
The issue is the P1206R7
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1206r7.pdf> and
the C++ Ranges Library do not define the std::ranges::container concept,
which would allow us to determine reliably from which ranges elements could
be safely moved out,
when that range is about to be destroyed.
Also, I see that for the purpose of specifying the std::ranges::to facility
in the C++23 standard,
there are two exposition-only concepts defined, which attempt to define
some sort of container concept,
but I don't think they are sufficient to detect whether a range is a
container or not:
- template< class Container >
constexpr bool reservable-container =
ranges::sized_range
<https://en.cppreference.com/w/cpp/ranges/sized_range.html><Container> &&
requires (Container& c, ranges::range_size_t
<https://en.cppreference.com/w/cpp/ranges/range_size_t.html><Container> n
)
{
c.reserve(n);
{ c.capacity() } -> std::same_as
<https://en.cppreference.com/w/cpp/concepts/same_as.html><decltype(n)>;
{ c.max_size() } -> std::same_as
<https://en.cppreference.com/w/cpp/concepts/same_as.html><decltype(n)>;
};
- template< class Container, class Reference >
constexpr bool container-appendable =
requires (Container& c, Reference&& ref)
{
requires
(
requires { c.emplace_back(std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; } ||
requires { c.push_back(std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; } ||
requires { c.emplace(c.end(), std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; } ||
requires { c.insert(c.end(), std::forward
<https://en.cppreference.com/w/cpp/utility/forward.html><Reference>(ref))
; }
);
};
Moreover, none of the existing concepts from the C++ Ranges Library are
suitable either:
- The std::ranges::view concept was redefined / relaxed multiple times,
but unfortunately the final version does not help us here,
since a range modelling the std::ranges::view concept
might not have reference semantics and might own its elements,
for example the std::ranges::owning_view and std::optional.
This is surprising and confusing in my opinion, considering that
views originally were supposed mean ranges referencing / pointing to
elements,
like std::span and std::string_view.
[image: ranges.png]
- The std::ranges::borrowed_range concept does not help us either,
because it's opt-in, that is, it does not detect automatically
whether a std::ranges::range owns its elements or just references them,
and does not decide based on that information whether a function
taking that range by-value can return an iterator obtained from that
range safely,
that is, without a danger of dangling.
Consequently, there might exist a range, which does not own its elements,
but does not opt-into the std::ranges::borrowed_range concept.
A good example is a custom span-like type written in C++17.
The ideas of ownership and value semantics are fundamental in C++ and Rust,
thus, in my opinion, the C++ Ranges Library should provide the std::ranges::
container concept.
What do you think?
Thank you, Mateusz Zych
Received on 2025-07-29 23:55:54