Reputation: 11317
#include <iostream>
#include <span>
#include <string>
#include <string_view>
#include <vector>
namespace {
template <typename TSpan>
auto createSubSpan1(TSpan &, typename TSpan::iterator start,
typename TSpan::iterator stop) {
static_assert(!std::is_convertible_v<typename TSpan::iterator, std::size_t>);
return TSpan{start, stop};
}
template <typename TSpan>
auto createSubSpan2(TSpan &span, typename TSpan::iterator start,
typename TSpan::iterator stop) {
auto startOffset = std::distance(span.begin(), start);
auto stopOffset = std::distance(span.begin(), stop);
std::cout << startOffset << "-" << stopOffset << std::endl;
static_assert(!std::is_convertible_v<decltype(span.data() + startOffset), std::size_t>);
return TSpan{span.data() + startOffset, span.data() + stopOffset};
}
template <typename TSpan>
auto createSubSpan3(TSpan &span, typename TSpan::iterator start,
typename TSpan::iterator stop) {
return span.subspan(static_cast<size_t>(std::distance(span.begin(), start)),
static_cast<size_t>(std::distance(start, stop)));
}
template <typename TSpan>
auto printSpan(std::string_view header, TSpan span) {
std::cout << header << std::endl;
for (const auto &element : span) std::cout << "\t" << element << std::endl;
}
} // namespace
int main() {
int a[]{0, 1, 2, 3, 4, 5, 6, 7, 8};
auto span = std::span{a};
std::vector<int> vector{11, 12, 13};
auto beginIt = span.begin();
auto endIt = std::move(vector.begin(), vector.end(), beginIt);
printSpan("incorrect (iterator)", createSubSpan1(span, beginIt, endIt));
printSpan("incorrect (pointer)", createSubSpan2(span, beginIt, endIt));
printSpan("correct", createSubSpan3(span, beginIt, endIt));
}
In this code, I would expect the createSubSpan1/2 methods to call constructor (3) of https://en.cppreference.com/w/cpp/container/span/span
template< class It, class End >
explicit(extent != std::dynamic_extent)
constexpr span( It first, End last );
However, for some reason both of them seem to print the complete span instead of the 3 elements I would expect. Variant 3 calling the subspan method does give the expected result.
Output of code with GCC, Clang and Clang+libc++:
incorrect (iterator)
11
12
13
3
4
5
6
7
8
0-3
incorrect (pointer)
11
12
13
3
4
5
6
7
8
correct
11
12
13
As far as I can tell, both should be contiguous_iterator, the end iterator is a sized_sentinel_for the start iterator (as it is the same type), and conversion of the returned reference type of the iterator (int &) is the element type given the qualification conversion AND the iterator/pointer are not convertible to size_t (see static_assert).
So from everything I can deduce, the mentioned constructor should be chosen and give me the expected effect. Can you explain why it does not?
Related questions:
Upvotes: 4
Views: 223
Reputation: 43026
In your example, the type of the span
variable is span<int, 9>
, so for createSubSpan1/2()
, TSpan
will be deduced as span<int, 9>
, which guarantees that the returned span has 9
elements. In other words, you must ensure that stop - start
is exactly 9
, otherwise you will get UB ([span.cons#8]).
As for createSubSpan3()
, since subspan()
returns span<int, dynamic_extent>
, the number of elements of the constructed span is exactly stop - start
.
Upvotes: 6