microsoft / GSL

Guidelines Support Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Conversion from span<dynamic_extent> to span<N> failes

beinhaerter opened this issue · comments

Initializing s2 succeeds, but initializing s3 fails.
Is this expected behaviour or a bug? At least for me it is surprising that I need to pass a dynamic extent span with exactly the same extent.

#include <array>
#include <gsl/span>

void f() {
    std::array<char, 20> a{};
    gsl::span<char> s1{a};
    gsl::span<char, 10> s2{s1.first(10)};  // succeeds
    gsl::span<char, 10> s3{s1};            // fails (std::terminate)
}

This Expects is causing the abort:

    template <std::size_t Ext>
    constexpr extent_type<Ext>::extent_type(extent_type<dynamic_extent> ext)
    {
        Expects(ext.size() == Ext);
    }

This appears to be expected behavior.

If you replace gsl::span with std::span in your example, the behavior is actually undefined:

#include <array>
#include <span>

void f() {
    std::array<char, 20> a{};
    gsl::span<char> s1{a};
    gsl::span<char, 10> s2{s1.first(10)}; 
    gsl::span<char, 10> s3{s1};  // undefined behavior
}

This is because when constructing a std::span of static extent from a std::span of dynamic extent

The behavior is undefined if both extent != dynamic_extent and source.size() != extent are true.

(https://en.cppreference.com/w/cpp/container/span/span)

When running that code with MSVC in debug mode, you actually get a runtime error "Cannot construct span with static extent from other span as other.size() != extent".

So this is a feature (defect?) of the std::span constructors.

... but ... since the behavior is technically "undefined" by std::span why can't we choose to implement it in a more convenient manner for gsl::span?
I imagine the choice to disallow it was made to keep the behavior consistent with how static-extent spans behave:

gsl::span<char, 20> s1{ a };
gsl::span<char, 19> x{ s1 }; // compile-time error, can't find appropriate constructor

std::span<char, 20> s2{ a };
std::span<char, 19> y{ s2 }; // compile-time error, can't find appropriate constructor

So, considering that fundamentally the design of gsl::span is such that it should match the interface of std::span exactly, while offering bounds checking, gsl::span raises the error you reported.