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).
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);
| ~^~~~~~~~
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.
Comments