|
|
|
|---|---|
|
lvalues of borrowed ranges |
lvalues of borrowed ranges |
/* capture of lvalue borrow */
std::string_view sv = "foo";
auto vue1 = sv | std::views::take(2);-- /* copy */
auto copy = vue1;Well-formed ( |
/* capture of lvalue */
std::string_view sv = "foo";
auto rad2 = sv | radr::take(2);Same syntax. /* copy */
auto copy = rad1;Well-formed ( |
|
lvalues of containers |
lvalues of containers |
/* capture of lvalue */
std::vector vec{1, 2, 3};
auto vue1 = vec | std::views::take(2);-- /* copy */
auto copy = vue1;Well-formed ( |
/* capture of lvalue */
std::vector vec{1, 2, 3};
auto rad2 = std::ref(vec) | radr::take(2);You need to use /* copy */
auto copy = rad1;Well-formed ( |
|
rvalues |
rvalues |
/* create adaptor on temporary */
auto vue0 = std::vector{1, 2, 3} | std::views::take(2);
/* move existing */
std::vector vec{1, 2, 3};
auto vue1 = std::move(vec) | std::views::take(2);Same syntax. /* copy */
// auto copy = vue1;Ill-formed, owning views are not copyable. |
/* create adaptor on temporary */
auto rad0 = std::vector{1, 2, 3} | radr::take(2);
/* move existing */
std::vector vec{1, 2, 3};
auto rad1 = std::move(vec) | radr::take(2);Same syntax. /* copy */
auto copy = rad1;Well-formed; our owning adaptors are ( |
See the page on fundamental range properties for more details.
|
|
|
|---|---|
|
dangling references |
dangling references |
{
std::string_view v{"FOO"};
/*…*/
return v | std::views::take(2);
}Well-formed; not dangling. {
std::string_view v{"FOO"};
/*…*/
return std::move(v) | std::views::take(2);
}Well-formed; not dangling. {
std::vector v{1,2,3};
/*…*/
return v | std::views::take(2);
}Well-formed, dangling 💣 {
std::vector v{1,2,3};
/*…*/
return std::move(v) | std::views::take(2);
}Well-formed, not dangling. |
{
std::string_view v{"FOO"};
/*…*/
return v | radr::take(2);
}Well-formed, not dangling. {
std::string_view v{"FOO"};
/*…*/
return std::move(v) | radr::take(2);
}Well-formed; not dangling. // {
// std::vector v{1,2,3};
// /*…*/
// return v | std::views::take(2);
// }Ill-formed; dangling prevented. {
std::vector v{1,2,3};
/*…*/
return std::move(v) | std::views::take(2);
}Well-formed; not dangling. |
This library's syntax explicitly forces you to choose between std::move()-ing or std::ref()-ing existing variables
into adaptors. This avoids unintendedly creating dangling references.
|
|
|
|---|---|
|
const-iterating |
const-iterating |
void print(auto const & r)
{
for (char c : r)
std::cout << c;
}-- std::vector vec{'f','o','o'};
auto v = vec | std::views::take(2);
print(v);Well-formed. std::vector vec{'f','o','o'};
auto v = vec | std::views::transform(/**/);
print(v);Well-formed. std::vector vec{'f','o','o'};
auto v = vec | std::views::filter(/**/);
// print(v);Ill-formed; filter not const-iterable. |
void print(auto const & r)
{
for (char c : r)
std::cout << c;
}-- std::vector vec{'f','o','o'};
auto v = vec | radr::take(2);
print(v);Well-formed. std::vector vec{'f','o','o'};
auto v = vec | radr::transform(/**/);
print(v);Well-formed. std::vector vec{'f','o','o'};
auto v = vec | radr::filter(/**/);
print(v);Well-formed. |
|
shallow const |
deep const |
void mutcon(auto const & r)
{
for (char & c : r)
c = 'A';
}-- std::vector vec{'f','o','o'};
// mutcon(v);Ill-formed; can't write through std::vector vec{'f','o','o'};
auto v = vec | std::views::take(2);
mutcon(v);Well-formed; vec is changed 🙈 |
void mutcon(auto const & r)
{
for (char & c : r)
c = 'A';
}-- std::vector vec{'f','o','o'};
// mutcon(v);Ill-formed; can't write through std::vector vec{'f','o','o'};
auto v = vec | std::views::take(2);
// mutcon(v);Ill-formed; can't write through |
- Many standard library views are not const-iterable; all our adaptors on containers are.
- Standard library views allow writing through
const; all our adaptors on containers forbid it. - See the page on const for more details.
|
|
|
|---|---|
|
undefined behaviour |
no undefined behaviour |
std::vector vec{1,2,2,3}
auto is_even = /**/;
auto vue = vec | std::view::filter(is_even);
auto b = vue.begin(); // on first '2'
*b = 1; // no longer satisfies predicateUndefined behaviour 💣 |
std::vector vec{1,2,2,3}
auto is_even = /**/;
auto rad = vec | radr::filter(is_even);
auto b = vue.begin(); // on first '2'
// *b = 1;Ill-formed; our filter prevents changes to vec. |
|
undefined behaviour |
no undefined behaviour |
std::vector vec{1,2,2,3}
auto plusCount = [count=0] (int i) mutable
{
return i + count++;
};
auto vue = vec | std::view::transform(plusCount);
std::print("{}", vue[0]); // "1"
std::print("{}", vue[0]); // "2"Violates the multi-pass guarantee. Undefined behaviour 💣 |
std::vector vec{1,2,2,3}
auto plusCount = [count=0] (int i) mutable
{
return i + count++;
};
// auto rad = std::ref(vec) | radr::transform(plusCount);Mutable function object is rejected; no undefined behaviour.1 |
See the page on undefined behaviour.
Footnotes
-
Note that we cannot prevent all violations of the multi-pass guarantee, e.g. if the count variable is captured from outside, the functor need not be mutable to violate the guarantee. But catching some is better than catching none 😉 . ↩