无法将右值 std::array 转换为 std::span
Cannot convert rvalue std::array to std::span
考虑以下代码。
#include <array>
#include <span>
std::array<int, 2> Foo()
{
return {1, 2};
}
int main()
{
std::array a = {3, 4};
std::span s1 = a;
// std::span s2 = Foo(); // Nope
// std::span s3 = std::move(a); // Nope
// std::span s4 = std::array<int, 2>{5, 6}; // Nope
// MSVC 19.29: 'initializing': cannot convert from 'std::array<int,2>' to 'std::span<int,18446744073709551615>'
// GCC 12.0.0: conversion from 'std::array<int, 2>' to non-scalar type 'std::span<int, 18446744073709551615>' requested
// clang 13.0.0: actually compiles!
}
似乎 std::array
可以转换为一个 std::span
,当它是一个仅在 clang 上的右值时。
我不确定这是否是问题的根源,但这是 MSVC 对 std::span
.
的 std::array
相关构造函数的实现
template <class _OtherTy, size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
&& is_convertible_v<_OtherTy (*)[], element_type (*)[]>
constexpr span(array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Arr.data(), _Size) {}
template <class _OtherTy, size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
&& is_convertible_v<const _OtherTy (*)[], element_type (*)[]>
constexpr span(const array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Arr.data(), _Size) {}
乍一看我没有发现任何问题。右值应该绑定到 const 左值引用。
我的问题是:这段代码应该编译(这是前一个编译器的问题)还是不编译(这是后一个编译器的问题)?
我不知道 std::array
到 std::span
的 r 值转换是否应该是一个编译错误,但这种转换很可能是一个错误,使用这样的跨度可能会导致 UB。
基本上,span
是查看器,而 array
是容器。 R 值被假定为临时值,因此,从临时容器初始化的 span
可能指向已销毁的数据片段,并且其用法未定义。
在某些情况下它是合法的操作 - 例如包装数组以进行调用。但总的来说,这是不安全和危险的。对于“array wrapping by span”,如果需要,你总是可以创建一个辅助函数。
长话短说;博士。这只是因为 libc++ 还没有实现
P1394.
问题是在 libstd++ and MSVC-STL 中,std::span
有以下 CTAD:
template<typename _Range>
span(_Range &&)
-> span<remove_reference_t<ranges::range_reference_t<_Range&>>>;
当我们调用std::span{std::array{0}}
时,span
的类型将被推导为span<int>
,我们将调用span<int>::span(const array<int, 1>&)
,但是这个构造函数有following constraints:
Constraints: Let U
be remove_pointer_t<decltype(data(arr))>
.
extent == dynamic_extent || N == extent
is true
, and
is_convertible_v<U(*)[], element_type(*)[]>
is true
.
由于arr.data()
的return类型是const int*
,element_type
是int
,所以is_convertible_v<const int(*)[], int(*)[]>
的值为false,所以不满足约束条件。
此 CTAD 在 libc++ 中不可用,因此 std::span{std::array{0}}
将使用以下 CTAD:
template<class _Tp, size_t _Sz>
span(const array<_Tp, _Sz>&) -> span<const _Tp, _Sz>;
然后调用 span<const int, 1>::span(array<const int, 1>&)
,这只是...工作。
考虑以下代码。
#include <array>
#include <span>
std::array<int, 2> Foo()
{
return {1, 2};
}
int main()
{
std::array a = {3, 4};
std::span s1 = a;
// std::span s2 = Foo(); // Nope
// std::span s3 = std::move(a); // Nope
// std::span s4 = std::array<int, 2>{5, 6}; // Nope
// MSVC 19.29: 'initializing': cannot convert from 'std::array<int,2>' to 'std::span<int,18446744073709551615>'
// GCC 12.0.0: conversion from 'std::array<int, 2>' to non-scalar type 'std::span<int, 18446744073709551615>' requested
// clang 13.0.0: actually compiles!
}
似乎 std::array
可以转换为一个 std::span
,当它是一个仅在 clang 上的右值时。
我不确定这是否是问题的根源,但这是 MSVC 对 std::span
.
std::array
相关构造函数的实现
template <class _OtherTy, size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
&& is_convertible_v<_OtherTy (*)[], element_type (*)[]>
constexpr span(array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Arr.data(), _Size) {}
template <class _OtherTy, size_t _Size>
requires (_Extent == dynamic_extent || _Extent == _Size)
&& is_convertible_v<const _OtherTy (*)[], element_type (*)[]>
constexpr span(const array<_OtherTy, _Size>& _Arr) noexcept : _Mybase(_Arr.data(), _Size) {}
乍一看我没有发现任何问题。右值应该绑定到 const 左值引用。
我的问题是:这段代码应该编译(这是前一个编译器的问题)还是不编译(这是后一个编译器的问题)?
我不知道 std::array
到 std::span
的 r 值转换是否应该是一个编译错误,但这种转换很可能是一个错误,使用这样的跨度可能会导致 UB。
基本上,span
是查看器,而 array
是容器。 R 值被假定为临时值,因此,从临时容器初始化的 span
可能指向已销毁的数据片段,并且其用法未定义。
在某些情况下它是合法的操作 - 例如包装数组以进行调用。但总的来说,这是不安全和危险的。对于“array wrapping by span”,如果需要,你总是可以创建一个辅助函数。
长话短说;博士。这只是因为 libc++ 还没有实现 P1394.
问题是在 libstd++ and MSVC-STL 中,std::span
有以下 CTAD:
template<typename _Range>
span(_Range &&)
-> span<remove_reference_t<ranges::range_reference_t<_Range&>>>;
当我们调用std::span{std::array{0}}
时,span
的类型将被推导为span<int>
,我们将调用span<int>::span(const array<int, 1>&)
,但是这个构造函数有following constraints:
Constraints: Let
U
beremove_pointer_t<decltype(data(arr))>
.
extent == dynamic_extent || N == extent
istrue
, andis_convertible_v<U(*)[], element_type(*)[]>
istrue
.
由于arr.data()
的return类型是const int*
,element_type
是int
,所以is_convertible_v<const int(*)[], int(*)[]>
的值为false,所以不满足约束条件。
此 CTAD 在 libc++ 中不可用,因此 std::span{std::array{0}}
将使用以下 CTAD:
template<class _Tp, size_t _Sz>
span(const array<_Tp, _Sz>&) -> span<const _Tp, _Sz>;
然后调用 span<const int, 1>::span(array<const int, 1>&)
,这只是...工作。