Background
So, my understanding is that deduction guides only work on constructors, and help in automatically deducing template parameters for the type of a constructor when the template type values are not automatically known.
In my own span class, I'm trying to make automatic deduction for my classes template parameters work from my classes constructor for rvalue std::array (and any rvalue contiguous sequence), ie so my_span{std::array{1,2,3}} works.
After running into issues with this, I realized neither GCC nor MSVC appear to allow this to work with std::span. To be clear, passing as a function parameter, or explicitly specifying types of std::span work. It just cannot seem to automatically create a std::span<const T> from a constructor call with rvalue std::array.
I had to double take trying to figure out if you could even use deduction on a raw constructor call, but it is clearly outlined in the examples given here. Below demonstrates similar functionality to what I'm asking for.
// declaration of the template
template<class T>
struct container
{
container(T t) {}
template<class Iter>
container(Iter beg, Iter end);
};
// additional deduction guide
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
// uses
container c(7); // OK: deduces T=int using an implicitly-generated guide
std::vector<double> v = {/* ... */};
auto d = container(v.begin(), v.end()); // OK: deduces T=double
container e{5, 6}; //
Errors
My understanding is the following deduction outlined in the C++ standard should apply:
template< class T, std::size_t N >
span( const std::array<T, N>& ) -> span<const T, N>;
However, this fails with the following code on both GCC and MSVC.
#include<span>
#include <vector>
#include <array>
void p(std::span<const int> s) {
}
int main(){
std::vector<int> init = {1,2,3,4};
std::span<int> x;
p(std::array<int, 3>{1,2,3}); //works
p({std::array{1,2,3}}); //works
auto std_span_0 = std::span(std::array{1,2,3}); //doesn't work compile errro
std::span std_span_1(std::array{1,2,3}) //doesn't work compile error
return 0;
}
gcc error
<source>:12:22: error: no matching conversion for functional-style cast from 'std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>' (aka 'array<int, 1 + sizeof...(_Up)>') to 'std::span<remove_reference_t<ranges::range_reference_t<array<int, 3> &>>>' (aka 'span<int>')
12 | auto std_span_0 = std::span(std::array{1,2,3});
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:194:2: note: candidate constructor [with _Tp = int, _ArrayExtent = 3] not viable: expects an lvalue for 1st argument
194 | span(array<_Tp, _ArrayExtent>& __arr) noexcept
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:225:7: note: candidate constructor not viable: no known conversion from 'std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>' (aka 'array<int, 1 + sizeof...(_Up)>') to 'const span<int>' for 1st argument
225 | span(const span&) noexcept = default;
| ^ ~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:187:2: note: candidate template ignored: could not match 'type_identity_t<element_type>[_ArrayExtent]' (aka 'int[_ArrayExtent]') against 'std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>' (aka 'array<int, 1 + sizeof...(_Up)>')
187 | span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:201:2: note: candidate template ignored: constraints not satisfied [with _Tp = int, _ArrayExtent = 3]
201 | span(const array<_Tp, _ArrayExtent>& __arr) noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:199:11: note: because '__is_compatible_array<const int, 3UL>::value' evaluated to false
199 | requires __is_compatible_array<const _Tp, _ArrayExtent>::value
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:213:2: note: candidate template ignored: constraints not satisfied [with _Range = std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>]
213 | span(_Range&& __range)
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:207:8: note: because '!__detail::__is_std_array<remove_cvref_t<array<int, 3> > >' evaluated to false
207 | && (!__detail::__is_std_array<remove_cvref_t<_Range>>)
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:233:2: note: candidate template ignored: could not match 'span' against 'array'
233 | span(const span<_OType, _OExtent>& __s) noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:149:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
149 | span() noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:157:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
157 | span(_It __first, size_type __count)
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:172:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
172 | span(_It __first, _End __last)
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~
<source>:13:14: error: no matching constructor for initialization of 'std::span<remove_reference_t<ranges::range_reference_t<array<int, 3> &>>>' (aka 'span<int>')
13 | std::span std_span_1(std::array{1,2,3});
| ^ ~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:194:2: note: candidate constructor [with _Tp = int, _ArrayExtent = 3] not viable: expects an lvalue for 1st argument
194 | span(array<_Tp, _ArrayExtent>& __arr) noexcept
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:225:7: note: candidate constructor not viable: no known conversion from 'std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>' (aka 'array<int, 1 + sizeof...(_Up)>') to 'const span<int>' for 1st argument
225 | span(const span&) noexcept = default;
| ^ ~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:187:2: note: candidate template ignored: could not match 'type_identity_t<element_type>[_ArrayExtent]' (aka 'int[_ArrayExtent]') against 'std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>' (aka 'array<int, 1 + sizeof...(_Up)>')
187 | span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:201:2: note: candidate template ignored: constraints not satisfied [with _Tp = int, _ArrayExtent = 3]
201 | span(const array<_Tp, _ArrayExtent>& __arr) noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:199:11: note: because '__is_compatible_array<const int, 3UL>::value' evaluated to false
199 | requires __is_compatible_array<const _Tp, _ArrayExtent>::value
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:213:2: note: candidate template ignored: constraints not satisfied [with _Range = std::array<enable_if_t<is_same_v<int, int> && is_same_v<int, int>, int>, 1 + sizeof...(_Up)>]
213 | span(_Range&& __range)
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:207:8: note: because '!__detail::__is_std_array<remove_cvref_t<array<int, 3> > >' evaluated to false
207 | && (!__detail::__is_std_array<remove_cvref_t<_Range>>)
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:233:2: note: candidate template ignored: could not match 'span' against 'array'
233 | span(const span<_OType, _OExtent>& __s) noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:149:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
149 | span() noexcept
| ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:157:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
157 | span(_It __first, size_type __count)
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/14.0.0/../../../../include/c++/14.0.0/span:172:2: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
172 | span(_It __first, _End __last)
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.
Compiler returned: 1
It doesn't really explain why the constructor was rejected here.
MSVC similarly fails:
example.cpp
<source>(12): error C2440: '<function-style-cast>': cannot convert from 'std::array<int,3>' to 'std::span<int,18446744073709551615>'
<source>(12): note: 'std::span<int,18446744073709551615>::span': no overloaded function could convert all the argument types
C:/data/msvc/14.39.33321-Pre/include\span(358): note: could be 'std::span<int,18446744073709551615>::span<int,3>(std::array<int,3> &) noexcept'
<source>(12): note: 'std::span<int,18446744073709551615>::span<int,3>(std::array<int,3> &) noexcept': cannot convert argument 1 from 'std::array<int,3>' to 'std::array<int,3> &'
<source>(12): note: A non-const reference may only be bound to an lvalue
C:/data/msvc/14.39.33321-Pre/include\span(379): note: or 'std::span<int,18446744073709551615>::span(const std::span<_OtherTy,_OtherExtent> &) noexcept'
C:/data/msvc/14.39.33321-Pre/include\span(366): note: or 'std::span<int,18446744073709551615>::span(_Rng &&)'
C:/data/msvc/14.39.33321-Pre/include\span(363): note: or 'std::span<int,18446744073709551615>::span(const std::array<_OtherTy,_Size> &) noexcept'
C:/data/msvc/14.39.33321-Pre/include\span(353): note: or 'std::span<int,18446744073709551615>::span(int (&)[_Size]) noexcept'
C:/data/msvc/14.39.33321-Pre/include\span(339): note: or 'std::span<int,18446744073709551615>::span(_It,_Sentinel) noexcept(<expr>)'
C:/data/msvc/14.39.33321-Pre/include\span(328): note: or 'std::span<int,18446744073709551615>::span(_It,std::span<int,18446744073709551615>::size_type) noexcept'
<source>(12): note: while trying to match the argument list '(std::array<int,3>)'
<source>(13): error C2665: 'std::span<int,18446744073709551615>::span': no overloaded function could convert all the argument types
C:/data/msvc/14.39.33321-Pre/include\span(358): note: could be 'std::span<int,18446744073709551615>::span<int,3>(std::array<int,3> &) noexcept'
<source>(13): note: 'std::span<int,18446744073709551615>::span<int,3>(std::array<int,3> &) noexcept': cannot convert argument 1 from 'std::array<int,3>' to 'std::array<int,3> &'
<source>(13): note: A non-const reference may only be bound to an lvalue
C:/data/msvc/14.39.33321-Pre/include\span(379): note: or 'std::span<int,18446744073709551615>::span(const std::span<_OtherTy,_OtherExtent> &) noexcept'
<source>(13): note: 'std::span<int,18446744073709551615>::span(const std::span<_OtherTy,_OtherExtent> &) noexcept': could not deduce template argument for 'const std::span<_OtherTy,_OtherExtent> &' from 'std::array<int,3>'
C:/data/msvc/14.39.33321-Pre/include\span(366): note: or 'std::span<int,18446744073709551615>::span(_Rng &&)'
<source>(13): note: the associated constraints are not satisfied
C:/data/msvc/14.39.33321-Pre/include\span(365): note: the concept 'std::_Span_compatible_range<std::array<int,3>,int>' evaluated to false
C:/data/msvc/14.39.33321-Pre/include\span(254): note: the constraint was not satisfied
C:/data/msvc/14.39.33321-Pre/include\span(363): note: or 'std::span<int,18446744073709551615>::span(const std::array<_OtherTy,_Size> &) noexcept'
<source>(13): note: the associated constraints are not satisfied
C:/data/msvc/14.39.33321-Pre/include\span(362): note: the constraint was not satisfied
C:/data/msvc/14.39.33321-Pre/include\span(353): note: or 'std::span<int,18446744073709551615>::span(int (&)[_Size]) noexcept'
<source>(13): note: 'std::span<int,18446744073709551615>::span(int (&)[_Size]) noexcept': could not deduce template argument for 'int (&)[_Size]' from 'std::array<int,3>'
C:/data/msvc/14.39.33321-Pre/include\span(339): note: or 'std::span<int,18446744073709551615>::span(_It,_Sentinel) noexcept(<expr>)'
<source>(13): note: 'std::span<int,18446744073709551615>::span(_It,_Sentinel) noexcept(<expr>)': expects 2 arguments - 1 provided
C:/data/msvc/14.39.33321-Pre/include\span(328): note: or 'std::span<int,18446744073709551615>::span(_It,std::span<int,18446744073709551615>::size_type) noexcept'
<source>(13): note: 'std::span<int,18446744073709551615>::span(_It,std::span<int,18446744073709551615>::size_type) noexcept': expects 2 arguments - 1 provided
<source>(13): note: while trying to match the argument list '(std::array<int,3>)'
Compiler returned: 2
Question
Can someone show me how to get something like auto x = custom_span(std::array{1,2,3}} to work to correctly produce a custom_span<const int, 3> via ctad?
With just the deduction guide
It works as expected Demo
Issue is that constructors of span are more restricted than the only signature shown.
There is also a note:
And error states that an "extra" concept fails:
rejecting the expected constructor.