0

Is there a way to pass an empty std::span<int> to a function?

I have a function like below:

bool func( const std::vector<int>& indices )
{
    if ( !indices.empty( ) )
    {
        /* do something */
    }

    ...
}


// when calling it with an empty vector
const bool isAcceptable { func( std::vector<int>( 0 ) ) };

And I want to change it to use std::span instead of std::vector so that it can also get std::array and raw array as its argument.

Now here:

bool func( const std::span<const int> indices )
{
    if ( !indices.empty( ) )
    {
        /* do something */
    }

    ...
}


// when calling it with an empty span
const bool isAcceptable { func( std::span<int>( ) ) }; // Is this valid code?

Also does std::span properly support all contiguous containers (e.g. std::vector, std::array, etc.)?

digito_evo
  • 3,216
  • 2
  • 14
  • 42
  • 2
    *Is this valid code?* - Why do you think it might be invalid? `std::span`'s default ctor is documented to create an empty span. – Evg Dec 26 '21 at 16:54
  • @Evg So an empty `std::span` does not point to any location, right? Also is creating a temporary span object snd then passing it to the function (like above) the optimal way to achieve this? – digito_evo Dec 26 '21 at 16:58
  • 1
    Please define "optimal". To me, it's a good and clear way. – Evg Dec 26 '21 at 17:01
  • @Evg Optimal like being efficient (in terms of memory). Anyway, I got it. Thanks for your help. – digito_evo Dec 26 '21 at 17:03
  • 1
    "*Also does std::span properly support all contiguous containers?*" Not all, this container may also need be `sized_range` since the `span` will use `ranges::size` to get its size. – 康桓瑋 Dec 26 '21 at 17:04

1 Answers1

2

std::span's default constuctor is documented as:

constexpr span() noexcept;

Constructs an empty span whose data() == nullptr and size() == 0.

Hence, passing a default constructed std::span<int>() is well-defined. Calling empty() on it is guaranteed to return true.


Does std::span properly support all contiguous containers (e.g. std::vector, std::array, etc.)?

Basically, std::span can be constructed from anything that models a contiguous and sized range:

template<class R>
explicit(extent != std::dynamic_extent)
constexpr span(R&& range);

Constructs a span that is a view over the range range; the resulting span has size() == std::ranges::size(range) and data() == std::ranges::data(range).

In particular, std::vector does satisfy these requirements.

For C-style arrays and std::array there are special constructors (to harness their compile-time size):

template<std::size_t N>
constexpr span(element_type (&arr)[N]) noexcept;
template<class U, std::size_t N>
constexpr span(std::array<U, N>& arr) noexcept;
template<class U, std::size_t N>
constexpr span(const std::array<U, N>& arr) noexcept;

Constructs a span that is a view over the array arr; the resulting span has size() == N and data() == std::data(arr).

Evg
  • 25,259
  • 5
  • 41
  • 83
  • Well, this is what I was looking for. But another question of mine is that what does this mean? "This overload participates in overload resolution only if extent == 0 || extent == std::dynamic_extent." This is from the same link you provided. I mean what is `extent`? – digito_evo Dec 26 '21 at 17:07
  • So is `std::span` useless when it comes to associative containers? – digito_evo Dec 26 '21 at 17:19
  • 1
    @digito_evo `extent` is a second template parameter of `std::span` that is defaulted to `std::dynamic_extent`. `std::span` can have either a compile-time size (= `extent != std::dynamic_extent`) or run-time size (when `extent = std::dynamic_extent`). – Evg Dec 26 '21 at 17:19
  • 2
    @digito_evo Basically, yes. Associative containers are node-based and hence are not contiguous. You can form a span of size 1 to each particular element, though. ;) – Evg Dec 26 '21 at 17:20
  • Nice, so e.g. when a `std::array` is passed to my function, then `extent != std::dynamic_extent`? – digito_evo Dec 26 '21 at 17:24
  • @digito_evo No. Your function is declared as taking `std::span`, so `extent` is always equal to `std::dynamic_extent`. – Evg Dec 26 '21 at 17:30
  • I see, so there is no way in this particular case to make it compile-time since it also needs to accept vectors. – digito_evo Dec 26 '21 at 17:32
  • 1
    @digito_evo With that signature, no. – Evg Dec 26 '21 at 17:34