Skip to content

Commit 8183b4b

Browse files
Improve the std::span section.
1 parent b6c4515 commit 8183b4b

File tree

2 files changed

+66
-50
lines changed

2 files changed

+66
-50
lines changed

CPP20.md

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -482,40 +482,48 @@ std::osyncstream{std::cout} << "The value of x is:" << x << std::endl;
482482
```
483483

484484
### std::span
485-
A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy -- a simplified way to think about views is they are holding references to their data. Spans can be dynamically-sized or fixed-sized.
485+
A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy -- a simplified way to think about views is they are holding references to their data. As opposed to maintaining a pointer/iterator and length field, a span wraps both of those up in a single object.
486+
487+
Spans can be dynamically-sized or fixed-sized (known as their *extent*). Fixed-sized spans benefit from bounds-checking.
488+
489+
Span doesn't propogate const so to construct a read-only span use `std::span<const T>`.
490+
491+
Example: using a dynamically-sized span to print integers from various containers.
486492
```c++
487-
void f(std::span<int> ints) {
488-
std::for_each(ints.begin(), ints.end(), [](auto i) {
489-
// ...
490-
});
493+
void print_ints(std::span<const int> ints) {
494+
for (const auto n : ints) {
495+
std::cout << n << std::endl;
496+
}
491497
}
492498

493-
std::vector<int> v = {1, 2, 3};
494-
f(v);
495-
std::array<int, 3> a = {1, 2, 3};
496-
f(a);
499+
print_ints(std::vector{ 1, 2, 3 });
500+
print_ints(std::array<int, 5>{ 1, 2, 3, 4, 5 });
501+
502+
int a[10] = { 0 };
503+
print_ints(a);
497504
// etc.
498505
```
499-
Example: as opposed to maintaining a pointer and length field, a span wraps both of those up in a single container.
506+
507+
Example: a statically-sized span will fail to compile for containers that don't match the extent of the span.
500508
```c++
501-
constexpr size_t LENGTH_ELEMENTS = 3;
502-
int* arr = new int[LENGTH_ELEMENTS]; // arr = {0, 0, 0}
509+
void print_three_ints(std::span<const int, 3> ints) {
510+
for (const auto n : ints) {
511+
std::cout << n << std::endl;
512+
}
513+
}
503514
504-
// Fixed-sized span which provides a view of `arr`.
505-
std::span<int, LENGTH_ELEMENTS> span = arr;
506-
span[1] = 1; // arr = {0, 1, 0}
515+
print_three_ints(std::vector{ 1, 2, 3 }); // ERROR
516+
print_three_ints(std::array<int, 5>{ 1, 2, 3, 4, 5 }); // ERROR
517+
int a[10] = { 0 };
518+
print_three_ints(a); // ERROR
507519
508-
// Dynamic-sized span which provides a view of `arr`.
509-
std::span<int> d_span = arr;
510-
span[0] = 1; // arr = {1, 1, 0}
511-
```
512-
```c++
513-
constexpr size_t LENGTH_ELEMENTS = 3;
514-
int* arr = new int[LENGTH_ELEMENTS];
520+
std::array<int, 3> b = { 1, 2, 3 };
521+
print_three_ints(b); // OK
515522
516-
std::span<int, LENGTH_ELEMENTS> span = arr; // OK
517-
std::span<double, LENGTH_ELEMENTS> span2 = arr; // ERROR
518-
std::span<int, 1> span3 = arr; // ERROR
523+
// You can construct a span manually if required:
524+
std::vector c{ 1, 2, 3 };
525+
print_three_ints(std::span<const int, 3>{ c.data(), 3 }); // OK: set pointer and length field.
526+
print_three_ints(std::span<const int, 3>{ c.cbegin(), c.cend() }); // OK: use iterator pairs.
519527
```
520528

521529
### Bit operations

README.md

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -583,40 +583,48 @@ std::osyncstream{std::cout} << "The value of x is:" << x << std::endl;
583583
```
584584

585585
### std::span
586-
A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy -- a simplified way to think about views is they are holding references to their data. Spans can be dynamically-sized or fixed-sized.
586+
A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy -- a simplified way to think about views is they are holding references to their data. As opposed to maintaining a pointer/iterator and length field, a span wraps both of those up in a single object.
587+
588+
Spans can be dynamically-sized or fixed-sized (known as their *extent*). Fixed-sized spans benefit from bounds-checking.
589+
590+
Span doesn't propogate const so to construct a read-only span use `std::span<const T>`.
591+
592+
Example: using a dynamically-sized span to print integers from various containers.
587593
```c++
588-
void f(std::span<int> ints) {
589-
std::for_each(ints.begin(), ints.end(), [](auto i) {
590-
// ...
591-
});
594+
void print_ints(std::span<const int> ints) {
595+
for (const auto n : ints) {
596+
std::cout << n << std::endl;
597+
}
592598
}
593599

594-
std::vector<int> v = {1, 2, 3};
595-
f(v);
596-
std::array<int, 3> a = {1, 2, 3};
597-
f(a);
600+
print_ints(std::vector{ 1, 2, 3 });
601+
print_ints(std::array<int, 5>{ 1, 2, 3, 4, 5 });
602+
603+
int a[10] = { 0 };
604+
print_ints(a);
598605
// etc.
599606
```
600-
Example: as opposed to maintaining a pointer and length field, a span wraps both of those up in a single container.
607+
608+
Example: a statically-sized span will fail to compile for containers that don't match the extent of the span.
601609
```c++
602-
constexpr size_t LENGTH_ELEMENTS = 3;
603-
int* arr = new int[LENGTH_ELEMENTS]; // arr = {0, 0, 0}
610+
void print_three_ints(std::span<const int, 3> ints) {
611+
for (const auto n : ints) {
612+
std::cout << n << std::endl;
613+
}
614+
}
604615
605-
// Fixed-sized span which provides a view of `arr`.
606-
std::span<int, LENGTH_ELEMENTS> span = arr;
607-
span[1] = 1; // arr = {0, 1, 0}
616+
print_three_ints(std::vector{ 1, 2, 3 }); // ERROR
617+
print_three_ints(std::array<int, 5>{ 1, 2, 3, 4, 5 }); // ERROR
618+
int a[10] = { 0 };
619+
print_three_ints(a); // ERROR
608620
609-
// Dynamic-sized span which provides a view of `arr`.
610-
std::span<int> d_span = arr;
611-
span[0] = 1; // arr = {1, 1, 0}
612-
```
613-
```c++
614-
constexpr size_t LENGTH_ELEMENTS = 3;
615-
int* arr = new int[LENGTH_ELEMENTS];
621+
std::array<int, 3> b = { 1, 2, 3 };
622+
print_three_ints(b); // OK
616623
617-
std::span<int, LENGTH_ELEMENTS> span = arr; // OK
618-
std::span<double, LENGTH_ELEMENTS> span2 = arr; // ERROR
619-
std::span<int, 1> span3 = arr; // ERROR
624+
// You can construct a span manually if required:
625+
std::vector c{ 1, 2, 3 };
626+
print_three_ints(std::span<const int, 3>{ c.data(), 3 }); // OK: set pointer and length field.
627+
print_three_ints(std::span<const int, 3>{ c.cbegin(), c.cend() }); // OK: use iterator pairs.
620628
```
621629

622630
### Bit operations

0 commit comments

Comments
 (0)