Deducing extent when passing std::array to function expecting std::span

JayDee

I have a function expecting a std::span parameter. This function is called by passing an std::array. This works fine if the std::span argument is declared with template parameter Extent set to std::dynamic_extent. But if the function is templated on Extent, the compiler is unable to deduce this value from std::array argument.

Any idea why the compiler is unable to deduce this template argument ?

See the snippet code below (Godbolt link).

Update after comments

When we explicitly create a std::span (without specifying template parameters) object when calling g(), everything works fine, the compiler is able to deduce the span type and Extent from the std::array. Following cppreference, the corresponding constructor (5) is not mark as explicit.

So why is the compiler unable to do the same deduction ?

void f(const std::span<std::byte> buffer)
{
    std::cout << "f() => Extent = 0x" << std::hex << buffer.extent
              << " Size = " << std::dec << buffer.size() << std::endl;
}

template<std::size_t Extent>
void g(const std::span<std::byte, Extent> buffer)
{
    std::cout << "g() => Extent = 0x" << std::hex << buffer.extent
              << " Size = " << std::dec << buffer.size() << std::endl;
}

std::array<std::byte, 4> buffer = {};

f(buffer);
g(std::span{buffer}); // <= Works fine
g(buffer);            // <= Compiler error!

With this result from compiler:

<source>:23:6: error: no matching function for call to 'g(std::array<std::byte, 4>&)'
   23 |     g(buffer);
      |     ~^~~~~~~~
<source>:13:6: note: candidate: 'template<long unsigned int Extent> void g(std::span<std::byte, _Count>)'
   13 | void g(const std::span<std::byte, Extent> buffer)
      |      ^
<source>:13:6: note:   template argument deduction/substitution failed:
<source>:23:6: note:   'std::array<std::byte, 4>' is not derived from 'std::span<std::byte, _Count>'
   23 |     g(buffer);
      |     ~^~~~~~~~
Nicol Bolas

So why is the compiler unable to do the same deduction ?

Because class template argument deduction is a different process from function template argument deduction.

In the case of g, the compiler has no idea what the constructors of span are because... it's not a class yet. It's a template. And since template specialization is a thing, it is entirely possible that the deduced Extent value radically changes what span conversion constructors are available.

Function template argument deduction is about matching the type of each function argument against the type and template parameters of the corresponding parameter type. Since you're using a specific template type whose arguments have to be deduced from those of the incoming parameter, this requires that the parameter be a specialization of that template.

Class template argument deduction bypasses all of this because it is solely based on the constructor declarations in the primary template (and any template deduction guides). It can use the constructors in that template to deduce the template parameters.

But function template argument deduction doesn't work that way.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Passing a closure to a function expecting a std::ops::Fn

Deducing lambdas to std::function<T>

Using std extent with a template array

What's the purpose of std::dynamic_extent in std::span

deducing operator() with std::invoke

Passing a string to function but expecting an array

Passing a std::array of unknown size to a function

Alternative to std::bind when passing member function

get second dimension of the array using std::extent

Is the current standard specification on std::span default constructor correct on "Extent <= 0"?

Construct static extent of std::span from `{pointer, length}`

Why can T not be deduced for std::span<T> when passing a std::vector?

Cannot convert rvalue std::array to std::span

Error when passing an Object to a function expecting a Dictionnary?

Deducing std::string from const char* or char* when creating a std::tuple

C++ - Specify Overloaded Functor or Function When Passing To `std::function`

Passing a function object to std::function

std::atomic to what extent?

Passing result of std::bind to std::function "overloads"

Passing a char array to a function that expects a const std::string reference

Type deduction when passing lambda into variadic std::function

Deducing return and parameter type from std::function passed as a template function argument?

std::functions and lambda function passing

passing pointer to std::function<void()>

passing std::function* to template parameter

crash connected to passing std::array

Why does array match the std::extent second imp?

Deducing this lambda and std::invoke_result_t

Advantages of std::string_view when passing to another function taking std::string