c++ 为什么std::span的构造函数在使用2个指针/迭代器构造时不限制元素

j7dteeu8  于 2023-02-26  发布在  其他
关注(0)|答案(1)|浏览(128)
#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));
}

code at compiler-explorer
在这段代码中,我希望createSubSpan1/2方法调用https://en.cppreference.com/w/cpp/container/span/span的构造函数(3

template< class It, class End >
explicit(extent != std::dynamic_extent)
constexpr span( It first, End last );

然而,由于某种原因,它们似乎都打印了完整的span,而不是我所期望的3个元素。变量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

据我所知,两者都应该是contiguous_iterator,结束迭代器是开始迭代器的size_sentinel_(因为它是相同的类型),迭代器返回的引用类型(int &)的转换是给定限定转换的元素类型,并且迭代器/指针不能转换为size_t(参见static_assert)。
所以从我所能推断的一切来看,上面提到的构造函数应该被选择并给我预期的效果。你能解释一下为什么没有吗?
相关问题:

dsekswqp

dsekswqp1#

在您的示例中,span变量的类型是span<int, 9>,因此对于createSubSpan1/2()TSpan将被推导为span<int, 9>,这保证了返回的span具有9元素,换句话说,您必须确保stop - start正好是9,否则您将得到UB([span.cons#8])。
对于createSubSpan3(),由于subspan()返回span<int, dynamic_extent>,因此构造的span的元素数正好是stop - start

相关问题