c++templatesc++20c++-conceptsstd-span

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


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?


Solution

  • 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