#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));
}
Код в компиляторе-проводнике
В этом коде я ожидаю, что методы createSubSpan1/2 вызовут конструктор (3) 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 );
Однако по какой-то причине они оба печатают полный диапазон вместо трех элементов, которые я ожидал. Вариант 3, вызывающий метод subspan, дает ожидаемый результат.
Вывод кода с GCC, Clang и 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
Итак, из всего, что я могу сделать, следует выбрать упомянутый конструктор и дать мне ожидаемый эффект. Можете ли вы объяснить, почему это не так?
Связанные вопросы:
В вашем примере тип переменной span — span<int, 9>, поэтому для createSubSpan1/2() TSpan будет выведен как span<int, 9>, что гарантирует, что возвращаемый диапазон имеет 9 элементов. Другими словами, вы должны убедиться, что stop - start точно равно 9, иначе вы получите UB ([span.cons#8]).
Что касается createSubSpan3(), поскольку subspan() возвращает span<int, dynamic_extent>, количество элементов построенного промежутка равно stop - start.
K, поэтому, если бы a был бы std::vector, этой проблемы не возникло бы. Ни один из методов не использует std::span iso TSpan.
Звучит как поток проектирования в std::span, который даже позволяет указать размер, если не используется dynamic_extend. Спасибо за ответ, я не понимал, что использую фиксированный размер.
Короче говоря, вы нарушаете это правило в первых двух случаях: eel.is/c++draft/span.cons#8.1 (extent равно 9, а last-first равно 3).