|
21 | 21 | /** A Span is an object that can refer to a contiguous sequence of objects.
|
22 | 22 | *
|
23 | 23 | * It implements a subset of C++20's std::span.
|
| 24 | + * |
| 25 | + * Things to be aware of when writing code that deals with Spans: |
| 26 | + * |
| 27 | + * - Similar to references themselves, Spans are subject to reference lifetime |
| 28 | + * issues. The user is responsible for making sure the objects pointed to by |
| 29 | + * a Span live as long as the Span is used. For example: |
| 30 | + * |
| 31 | + * std::vector<int> vec{1,2,3,4}; |
| 32 | + * Span<int> sp(vec); |
| 33 | + * vec.push_back(5); |
| 34 | + * printf("%i\n", sp.front()); // UB! |
| 35 | + * |
| 36 | + * may exhibit undefined behavior, as increasing the size of a vector may |
| 37 | + * invalidate references. |
| 38 | + * |
| 39 | + * - One particular pitfall is that Spans can be constructed from temporaries, |
| 40 | + * but this is unsafe when the Span is stored in a variable, outliving the |
| 41 | + * temporary. For example, this will compile, but exhibits undefined behavior: |
| 42 | + * |
| 43 | + * Span<const int> sp(std::vector<int>{1, 2, 3}); |
| 44 | + * printf("%i\n", sp.front()); // UB! |
| 45 | + * |
| 46 | + * The lifetime of the vector ends when the statement it is created in ends. |
| 47 | + * Thus the Span is left with a dangling reference, and using it is undefined. |
| 48 | + * |
| 49 | + * - Due to Span's automatic creation from range-like objects (arrays, and data |
| 50 | + * types that expose a data() and size() member function), functions that |
| 51 | + * accept a Span as input parameter can be called with any compatible |
| 52 | + * range-like object. For example, this works: |
| 53 | +* |
| 54 | + * void Foo(Span<const int> arg); |
| 55 | + * |
| 56 | + * Foo(std::vector<int>{1, 2, 3}); // Works |
| 57 | + * |
| 58 | + * This is very useful in cases where a function truly does not care about the |
| 59 | + * container, and only about having exactly a range of elements. However it |
| 60 | + * may also be surprising to see automatic conversions in this case. |
| 61 | + * |
| 62 | + * When a function accepts a Span with a mutable element type, it will not |
| 63 | + * accept temporaries; only variables or other references. For example: |
| 64 | + * |
| 65 | + * void FooMut(Span<int> arg); |
| 66 | + * |
| 67 | + * FooMut(std::vector<int>{1, 2, 3}); // Does not compile |
| 68 | + * std::vector<int> baz{1, 2, 3}; |
| 69 | + * FooMut(baz); // Works |
| 70 | + * |
| 71 | + * This is similar to how functions that take (non-const) lvalue references |
| 72 | + * as input cannot accept temporaries. This does not work either: |
| 73 | + * |
| 74 | + * void FooVec(std::vector<int>& arg); |
| 75 | + * FooVec(std::vector<int>{1, 2, 3}); // Does not compile |
| 76 | + * |
| 77 | + * The idea is that if a function accepts a mutable reference, a meaningful |
| 78 | + * result will be present in that variable after the call. Passing a temporary |
| 79 | + * is useless in that context. |
24 | 80 | */
|
25 | 81 | template<typename C>
|
26 | 82 | class Span
|
|
0 commit comments