Why does `std::span` require an explicit constructor to use in a templated function?

112 Views Asked by At

Godbolt link.

I have the following function:

#include <algorithm>
#include <iostream>
#include <numeric>
#include <ranges>
#include <span>
#include <type_traits>

template <typename T>
concept arithmetic = std::is_arithmetic_v<T>;

template <arithmetic T, std::size_t N>
auto sum(std::span<T, N> const a, 
         std::span<T, N> const b, 
         std::span<T, N> const c)
{
    std::ranges::transform(a, b, std::begin(c),
                           [](auto const a, auto const b) { return a + b; });


}

When I try to call it like the following:

std::array<int, 10> a{};
std::array<int, 10> b{};
std::array<int, 10> c{};

// more initialisation for a, b, and c
// ...

sum(a, b, c);

I receive the following template deduction error:

<source>:41:5: error: no matching function for call to 'sum'
    sum(a, b, c);
        ^~~
<source>:15:10: note: candidate template ignored: could not match 'span' against 'array'
    auto sum(span_const_of_const<T, N> a, 
             ^

I need to fix the call with explicit constructors to std::span, like so:

sum(std::span(a), std::span(b), std::span(c));

I was under the impression that std::span meant I didn't have to do this. What am I doing wrong here?

1

There are 1 best solutions below

0
Caleth On BEST ANSWER

Template argument deduction does not consider conversions. If you don't deduce the arguments then you don't have to specify span.

sum<int, 10>(a, b, c);

Alternatively, you could deduce the arguments and constrain them to allow construction of a std::span<arithmetic, N>

template <typename T>
concept arithmetic_span_like = std::ranges::range<T> 
    && arithmetic<std::ranges::range_value_t<T>> 
    && std::constructible_from<std::span<std::ranges::range_value_t<T>>, T>;

template <typename A, typename B>
concept same_size = arithmetic_span_like<A> 
    && arithmetic_span_like<B>
    && requires(A a, B b)
{
    std::ranges::size(a) == std::ranges::size(b);
};

template <arithmetic_span_like A, arithmetic_span_like B, arithmetic_span_like C>
requires same_size<A, B> && same_size<A, C>
auto sum(A&& a, B&& b, C&& c)
{
    std::ranges::transform(a, b, std::begin(c), std::plus{});
}

Godbolt link