I am trying to pass what I think is a prvalue into a range adapter closure object. It won't compile unless I bind a name to the initializer list and make it an lvalue. What is happening here?
#include <bits/stdc .h>
using namespace std;
int main(){
//why does this compile?
auto init_list = {1,2,4};
auto v = init_list | views::drop(1);
//but not this?
// auto v2 = initializer_list<int>{1,2,4} | views::drop(1);
//or this?
//auto v3 = views::all(initializer_list<int>{1,2,4}) | views::drop(1);
}
CodePudding user response:
When you use r | views::drop(1)
to create a new view
, range adaptors will automatically convert r
into a view
for you. This requires that the type of r
must model viewable_range
:
template<class T>
concept viewable_range =
range<T> &&
((view<remove_cvref_t<T>> && constructible_from<remove_cvref_t<T>, T>) ||
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
Note the last requirement:
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
Since the type of r
doesn't model a view
, which in your case is initializer_list
, it needs to be an lvalue, allowing us to freely take its address to construct a ref_view
without worrying about dangling.
Or, we need it to be movable
, which allows us to transfer its ownership to owning_view
, which means the following is well-formed:
auto r = std::vector{42} | views::drop(1)
But in this case, we also need it not to be a specialization of initializer_list
, because we can't transfer ownership of it, initializer_list
always copy. That's why your example fails.