c++ 如何做一个能正确执行右值数组的Ctrans的span类演绎?

gmol1639  于 2024-01-09  发布在  其他
关注(0)|答案(2)|浏览(171)

背景

所以,我的理解是,演绎指南只对构造函数起作用,当模板类型值不能自动知道时,它可以帮助自动推导出构造函数类型的模板参数。
在我自己的span类中,我试图从我的类构造函数中为rvalue std::array(和任何rvalue连续序列)自动推导我的类模板参数,即my_span{std::array{1,2,3}}工作。
在遇到这个问题之后,我意识到GCC和MSVC似乎都不允许这个与std::span一起工作。为了清楚,作为函数参数传递,或者显式指定std::span类型都可以。它似乎无法从右值为std::array的构造函数调用自动创建std::span<const T>
我不得不加倍尝试,试图弄清楚你是否可以在原始构造函数调用上使用演绎,但在here给出的示例中,它被清楚地概述了。下面演示了与我要求的类似的功能。

  1. // declaration of the template
  2. template<class T>
  3. struct container
  4. {
  5. container(T t) {}
  6. template<class Iter>
  7. container(Iter beg, Iter end);
  8. };
  9. // additional deduction guide
  10. template<class Iter>
  11. container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
  12. // uses
  13. container c(7); // OK: deduces T=int using an implicitly-generated guide
  14. std::vector<double> v = {/* ... */};
  15. auto d = container(v.begin(), v.end()); // OK: deduces T=double
  16. container e{5, 6}; //

字符串

错误

我的理解是C++标准中概述的以下推论应该适用:

  1. template< class T, std::size_t N >
  2. span( const std::array<T, N>& ) -> span<const T, N>;


但是,这在GCC和MSVC上都失败了。

  1. #include<span>
  2. #include <vector>
  3. #include <array>
  4. void p(std::span<const int> s) {
  5. }
  6. int main(){
  7. std::vector<int> init = {1,2,3,4};
  8. std::span<int> x;
  9. p(std::array<int, 3>{1,2,3}); //works
  10. p({std::array{1,2,3}}); //works
  11. auto std_span_0 = std::span(std::array{1,2,3}); //doesn't work compile errro
  12. std::span std_span_1(std::array{1,2,3}) //doesn't work compile error
  13. return 0;
  14. }


GCC错误

  1. <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>')
  2. 12 | auto std_span_0 = std::span(std::array{1,2,3});
  3. | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. /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
  5. 194 | span(array<_Tp, _ArrayExtent>& __arr) noexcept
  6. | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  7. /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
  8. 225 | span(const span&) noexcept = default;
  9. | ^ ~~~~~~~~~~~
  10. /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)>')
  11. 187 | span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
  12. | ^
  13. /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]
  14. 201 | span(const array<_Tp, _ArrayExtent>& __arr) noexcept
  15. | ^
  16. /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
  17. 199 | requires __is_compatible_array<const _Tp, _ArrayExtent>::value
  18. | ^
  19. /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)>]
  20. 213 | span(_Range&& __range)
  21. | ^
  22. /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
  23. 207 | && (!__detail::__is_std_array<remove_cvref_t<_Range>>)
  24. | ^
  25. /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'
  26. 233 | span(const span<_OType, _OExtent>& __s) noexcept
  27. | ^
  28. /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
  29. 149 | span() noexcept
  30. | ^
  31. /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
  32. 157 | span(_It __first, size_type __count)
  33. | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  34. /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
  35. 172 | span(_It __first, _End __last)
  36. | ^ ~~~~~~~~~~~~~~~~~~~~~~~~
  37. <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>')
  38. 13 | std::span std_span_1(std::array{1,2,3});
  39. | ^ ~~~~~~~~~~~~~~~~~
  40. /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
  41. 194 | span(array<_Tp, _ArrayExtent>& __arr) noexcept
  42. | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  43. /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
  44. 225 | span(const span&) noexcept = default;
  45. | ^ ~~~~~~~~~~~
  46. /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)>')
  47. 187 | span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
  48. | ^
  49. /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]
  50. 201 | span(const array<_Tp, _ArrayExtent>& __arr) noexcept
  51. | ^
  52. /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
  53. 199 | requires __is_compatible_array<const _Tp, _ArrayExtent>::value
  54. | ^
  55. /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)>]
  56. 213 | span(_Range&& __range)
  57. | ^
  58. /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
  59. 207 | && (!__detail::__is_std_array<remove_cvref_t<_Range>>)
  60. | ^
  61. /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'
  62. 233 | span(const span<_OType, _OExtent>& __s) noexcept
  63. | ^
  64. /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
  65. 149 | span() noexcept
  66. | ^
  67. /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
  68. 157 | span(_It __first, size_type __count)
  69. | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  70. /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
  71. 172 | span(_It __first, _End __last)
  72. | ^ ~~~~~~~~~~~~~~~~~~~~~~~~
  73. 2 errors generated.
  74. Compiler returned: 1


它并没有真正解释为什么构造函数在这里被拒绝。
MSVC同样失败:

  1. example.cpp
  2. <source>(12): error C2440: '<function-style-cast>': cannot convert from 'std::array<int,3>' to 'std::span<int,18446744073709551615>'
  3. <source>(12): note: 'std::span<int,18446744073709551615>::span': no overloaded function could convert all the argument types
  4. 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'
  5. <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> &'
  6. <source>(12): note: A non-const reference may only be bound to an lvalue
  7. C:/data/msvc/14.39.33321-Pre/include\span(379): note: or 'std::span<int,18446744073709551615>::span(const std::span<_OtherTy,_OtherExtent> &) noexcept'
  8. C:/data/msvc/14.39.33321-Pre/include\span(366): note: or 'std::span<int,18446744073709551615>::span(_Rng &&)'
  9. C:/data/msvc/14.39.33321-Pre/include\span(363): note: or 'std::span<int,18446744073709551615>::span(const std::array<_OtherTy,_Size> &) noexcept'
  10. C:/data/msvc/14.39.33321-Pre/include\span(353): note: or 'std::span<int,18446744073709551615>::span(int (&)[_Size]) noexcept'
  11. C:/data/msvc/14.39.33321-Pre/include\span(339): note: or 'std::span<int,18446744073709551615>::span(_It,_Sentinel) noexcept(<expr>)'
  12. 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'
  13. <source>(12): note: while trying to match the argument list '(std::array<int,3>)'
  14. <source>(13): error C2665: 'std::span<int,18446744073709551615>::span': no overloaded function could convert all the argument types
  15. 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'
  16. <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> &'
  17. <source>(13): note: A non-const reference may only be bound to an lvalue
  18. C:/data/msvc/14.39.33321-Pre/include\span(379): note: or 'std::span<int,18446744073709551615>::span(const std::span<_OtherTy,_OtherExtent> &) noexcept'
  19. <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>'
  20. C:/data/msvc/14.39.33321-Pre/include\span(366): note: or 'std::span<int,18446744073709551615>::span(_Rng &&)'
  21. <source>(13): note: the associated constraints are not satisfied
  22. 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
  23. C:/data/msvc/14.39.33321-Pre/include\span(254): note: the constraint was not satisfied
  24. C:/data/msvc/14.39.33321-Pre/include\span(363): note: or 'std::span<int,18446744073709551615>::span(const std::array<_OtherTy,_Size> &) noexcept'
  25. <source>(13): note: the associated constraints are not satisfied
  26. C:/data/msvc/14.39.33321-Pre/include\span(362): note: the constraint was not satisfied
  27. C:/data/msvc/14.39.33321-Pre/include\span(353): note: or 'std::span<int,18446744073709551615>::span(int (&)[_Size]) noexcept'
  28. <source>(13): note: 'std::span<int,18446744073709551615>::span(int (&)[_Size]) noexcept': could not deduce template argument for 'int (&)[_Size]' from 'std::array<int,3>'
  29. C:/data/msvc/14.39.33321-Pre/include\span(339): note: or 'std::span<int,18446744073709551615>::span(_It,_Sentinel) noexcept(<expr>)'
  30. <source>(13): note: 'std::span<int,18446744073709551615>::span(_It,_Sentinel) noexcept(<expr>)': expects 2 arguments - 1 provided
  31. 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'
  32. <source>(13): note: 'std::span<int,18446744073709551615>::span(_It,std::span<int,18446744073709551615>::size_type) noexcept': expects 2 arguments - 1 provided
  33. <source>(13): note: while trying to match the argument list '(std::array<int,3>)'
  34. Compiler returned: 2

提问

有人能告诉我如何让像auto x = custom_span(std::array{1,2,3}}这样的东西通过ctad正确地产生custom_span<const int, 3>吗?

lqfhib0f

lqfhib0f1#

这对std::span不起作用的原因是最后列出的扣除指南用于https://en.cppreference.com/w/cpp/container/span/deduction_guides

  1. template< class R >
  2. span( R&& ) -> span<std::remove_reference_t<std::ranges::range_reference_t<R>>>;

字符串
对于R = std::array<int, 3>,右值std::array<int, 3>R&&的匹配比转换为const std::array<int, 3>&更好。
这意味着std::span(std::array{1,2,3})推导出int对应T,而不是const int(以及std::dynamic_extent而不是3对应的区段),右值数组不能转换为const int
如果有一个推理指南,比如:

  1. template< class T, std::size_t N >
  2. span( std::array<T, N>&& ) -> span<const T, N>;


这将允许std::span(std::array{1, 2, 3})(成为std::span<const int, 3>(std::array{1, 2, 3}))。但这可能不可取,因为数组立即被销毁,留下一个悬空引用。

j7dteeu8

j7dteeu82#

仅凭演绎指南

  1. template<typename T, std::size_t N>
  2. my_span(std::array<T, N>&) -> my_span<T, N>;
  3. template<typename T, std::size_t N>
  4. my_span(const std::array<T, N>&) -> my_span<const T, N>;

字符串
它按预期工作Demo
问题是constructors of span比唯一显示的签名更受限制。

  1. template< class U, std::size_t N >
  2. constexpr span( std::array<U, N>& arr ) noexcept;
  3. // (5)
  4. template< class U, std::size_t N >
  5. constexpr span( const std::array<U, N>& arr ) noexcept;
  6. // (6)


还有一条注解:
只有当extent == std::dynamic_extent || N == extent为true并且从std::remove_pointer_t<decltype(data(arr))>到element_type的转换至多是限定转换时,这些重载才会参与重载解析。
error表示“extra”概念失败:
“std::_Span_compatible_rangestd::array<int,3,int>”的概念被评估为false
拒绝预期的构造函数。

展开查看全部

相关问题