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

JVA*_*pen 4 c++ c++20

#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));
}


Run Code Online (Sandbox Code Playgroud)

编译器资源管理器中的代码

在此代码中,我希望 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 );
Run Code Online (Sandbox Code Playgroud)

然而,由于某种原因,它们似乎都打印了完整的跨度,而不是我期望的 3 个元素。调用 subspan 方法的变体 3 确实给出了预期的结果。

使用 GCC、Clang 和 Clang+libc++ 的代码输出:

#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));
}


Run Code Online (Sandbox Code Playgroud)

据我所知,两者都应该是连续迭代器,结束迭代器是起始迭代器的 sized_sentinel_(因为它是相同的类型),迭代器返回的引用类型(int &)的转换是给定的元素类型限定转换和迭代器/指针不可转换为 size_t (请参阅 static_assert)。

因此,从我可以推断的一切来看,应该选择提到的构造函数并给我预期的效果。你能解释一下为什么不可以吗?

相关问题:

康桓瑋*_*康桓瑋 6

在您的示例中,变量的类型spanspan<int, 9>,因此 for createSubSpan1/2(),TSpan将被推导为span<int, 9>,这保证返回的跨度具有9元素。换句话说,你必须确保它stop - start是准确的9,否则你将得到 UB ( [span.cons#8] )。

至于createSubSpan3(),由于subspan()返回span<int, dynamic_extent>,构造的span的元素个数正好是stop - start