std::vector
and pretty much all other containers have a very convenient way of bounds checking: at()
. std::span
doesn't have that apparently.
- Why?
- Is there a replacement? Other than rolling out your own
at()
?
std::vector
and pretty much all other containers have a very convenient way of bounds checking: at()
. std::span
doesn't have that apparently.
at()
?Pretty clunky but something like this:
template<class Container>
auto& at(Container&& c, std::size_t pos){
if(pos >= c.size())
throw std::out_of_range("out of bounds");
return c[pos];
}
template<class Iterator, class Container>
auto& at(Container&& c, Iterator&& it){
if(std::distance(c.begin(), it) >= c.size())
throw std::out_of_range("out of bounds");
return *it;
}
Container&&
should be a forward reference, see here, so you don't need to redefine it for each const/non const ecc ecc type, infact const std::vector<int> v{1,2,3}; std::cout << at(v, v.end());
should works fine –
Born auto&
) to something that is going to perish in a moment. –
Lictor std::vector::operator[]
does the same. No? –
Shizue std::cout << std::vector<std::string>{"a","b","c"}[2];
or std::vector<std::string>{"a","b","c"}.at(2)
, so I think that is not that big of a deal –
Born &&
for rvalues? Like std::get
: it returns T&&
if given an rvalue tuple, or delete
an overload that takes rvalues, like std::ref
. –
Lictor std::distance(c.begin(), it) >= c.size()
and not it == c.end()
? std::distance
can take linear time, whereas iterator dereferencing is constant time. –
Lictor c.end()+10
, with the ==
you won't get the real bound checking –
Born c.end() + 10
is always undefined behaviour, even for std::vector
, and what happens after UB is irrelevant. –
Lictor at()
will get a size_t
, so probably the version with the iteration won't be used –
Born span
contains contagious memory container, which will have a constant distance evaluation, since you can probably just use the operator-
between the 2 iterators –
Born std::get()
does something different. Shouldn't free at()
do the similar thing? I'm just asking. –
Lictor operator-
looks like a good alternative. Then this (generic) code won't compile for iterators that don't support constant time std::distance
, and this is good. –
Lictor operator-
. However, since this is only meant for debugging, it shouldn't matter either way. –
Shizue -D_GLIBCXX_DEBUG
reports assertion violation for out-of-bounds []
access on std::span
: Assertion '__idx < size()' failed.
. –
Lictor The paper that introduced span to the standard library says:
Range-checking and bounds-safety
All accesses to the data encapsulated by a span are conceptually range-checked to ensure they remain within the bounds of the span. What actually happens as the result of a failure to meet span’s boundssafety constraints at runtime is undefined behavior.
That is, the operations have narrow contracts, giving freedom to the implementer.
If your standard library doesn't let you control the behavior to the appropriate granularity. gsl-lite offers drop-in replacement with configurable contract violation behavior. The Microsoft GSL was previously configurable, but now always terminates on contract violation, discussed here (which might actually be what you want).
std::vector::at
, I know it will be doing bounds checking whether I am using GCC, Clang, MSVC or anything. –
Shizue operator[]
, and alternatively we had a separate wider-contract function (at
) and I was wondering why this was not added to span
. –
Shizue at
for our containers like vector and basic_string –
Shizue at
throws an exception when the index is out of boundary. Going out of boundary in a span
is regarded as violation of contract, and the behaviour is not controlled in code—as you can configure for no action, exception throwing, or termination. –
Luck © 2022 - 2024 — McMap. All rights reserved.
at()
and alike exception based interfaces were a misconception which serves primarily to conceal logic errors further up at a significant overhead. – Lolitaat()
is great for debugging – Shizue