I have the following code where for some reason only last block does not work.
I originally thought it may be related to unique_ptr
being move only or std::set
having const keys, but then it is unclear why other blocks work.
namespace sr = std::ranges;
namespace sv = std::views;
int main() {
{
std::set<int> up_s{10,20};
const auto up_vec = sv::as_rvalue(up_s) | sr::to<std::vector>();
assert(up_vec.size() == 2);
}
{
std::vector<std::unique_ptr<int>> up_d;
up_d.emplace_back(std::make_unique<int>(10));
up_d.emplace_back(std::make_unique<int>(20));
const auto up_vec = sv::as_rvalue(up_d) | sr::to<std::vector>();
assert(up_vec.size() == 2);
}
{
std::set<std::unique_ptr<int>> up_d;
up_d.emplace(std::make_unique<int>(10));
up_d.emplace(std::make_unique<int>(20));
//const auto up_vec = sv::as_rvalue(up_d) | sr::to<std::vector>();
//assert(up_vec.size() == 2);
}
}
Error message seems useless, as it looks like that for some reason code wants to treat unique_ptr
as range
/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/ranges:9354:25: error: static assertion failed 9354 | static_assert(input_range<range_reference_t<_Rg>>); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/ranges:9354:25: note: constraints not satisfied
...
/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:499:13: required for the satisfaction of 'range<_Tp>' [with _Tp = const std::unique_ptr<int, std::default_delete >&&] /opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:499:21: in requirements with '_Tp& __t' [with _Tp = const std::unique_ptr<int, std::default_delete >&&] /opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:501:22: note: the required expression 'std::ranges::_Cpo::begin(__t)' is invalid 501 | ranges::begin(__t); | ~~~~~~~~~~~~~^~~~~ /opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:502:20: note: the required expression 'std::ranges::_Cpo::end(__t)' is invalid 502 | ranges::end(__t);
I originally thought it may be related to
unique_ptr
being move only orstd::set
havingconst
keys, but then it is unclear why other blocks work.
You've hit the nail on the head. It's very difficult to tell from the errors and I wasn't able to produce some error message that would make it obvious, but in short, the issue is that std::set
only gives us access to const
keys and std::unique_ptr
would require something non-const to make moving possible.
Piping this range through as_rvalue
doesn't fix this problem; it just means that we're working with a range of xvalues of type const std::unique_ptr
, but that's no good. The following code is ill-formed, and so is your attempt at making a vector:
const std::unique_ptr<int> p;
auto q = std::move(p); // call to deleted copy constructor, ill-formed
To get more into specifics, ranges::to<std::vector>
would require std::vector(std::from_range, sv::as_rvalue(up_d))
to be valid. However, the constraint container-compatible-range is not satisfied because const std::unique_ptr<int>&&
is not convertible to std::unique_ptr<int>
.
In the case of std::set<int>
, the constness doesn't matter.
std::set<int>
is range of const int
lvalues, and piping it through as_rvalue
makes it a range of const int
xvalues.
The following code is fine, and so is construction of a std::vector<int>
from such a range:
const int x = 0;
int y = std::move(x); // initialization of an int from an xvalue of type const int
In the case of std::vector<std::unique_ptr<int>>
, constness isn't a problem.
A std::vector
gives us non-const access to the elements, so piping it through as_rvalue
gives us a range of non-const std::unique_ptr
xvalues.
Therefore, the move constructor will be called for each element.