能否将int*[N]转为std::span<const int* const>?编译器兼容性疑问
关于std::span构造的编译器行为疑问
我编写了如下代码:
#include <span> int* pointers[3]; std::span<const int* const> s = pointers;
这段代码可通过GCC编译,但使用libstdc++的Clang编译时报错,错误信息如下:
<source>:4:29: error: no viable conversion from 'int *[3]' to 'std::span<const int *const>' 4 | std::span<const int* const> s = pointers; | ^ ~~~~~~~~ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:230:7: note: candidate constructor not viable: no known conversion from 'int *[3]' to 'const span<const int *const> &' for 1st argument 230 | span(const span&) noexcept = default; | ^ ~~~~~~~~~~~ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:192:2: note: candidate template ignored: could not match 'const int *' against 'int *' 192 | span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept | ^ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:199:2: note: candidate template ignored: could not match 'array<_Tp, _ArrayExtent>' against 'int *[3]' 199 | span(array<_Tp, _ArrayExtent>& __arr) noexcept | ^ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:206:2: note: candidate template ignored: could not match 'array<_Tp, _ArrayExtent>' against 'int *[3]' 206 | span(const array<_Tp, _ArrayExtent>& __arr) noexcept | ^ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:218:2: note: candidate template ignored: constraints not satisfied [with _Range = int *(&)[3]] 218 | span(_Range&& __range) | ^ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:213:8: note: because '!is_array_v<remove_cvref_t<int *(&)[3]>>' evaluated to false 213 | && (!is_array_v<remove_cvref_t<_Range>>) | ^ /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/span:238:2: note: candidate template ignored: could not match 'span<_OType, _OExtent>' against 'int *[3]' 238 | span(const span<_OType, _OExtent>& __s) noexcept | ^
我原本认为仅需限定转换(如int*转为const int* const)时,std::span可从范围构造,请问哪个编译器的行为是正确的?考虑到使用-stdlib=libc++时代码可编译,这很可能是Clang与GCC的互操作性问题。
解答
GCC的行为符合C标准,使用libstdc的Clang存在实现缺陷。
原因如下:
- 当用数组初始化
std::span<const int* const>时,标准允许通过数组到指针的转换,再调用std::span接受指针和大小的构造函数完成初始化。int*[]可以隐式转换为int* const*,而int* const*又能转换为const int* const*——给指向的元素添加const限定是C++允许的隐式转换。 - libstdc的
std::span实现中,针对数组的构造模板(错误信息中第192行的重载)要求数组元素类型与element_type严格匹配,忽略了允许隐式转换的场景。而GCC的实现则支持这种隐式转换,libc的实现也正确处理了该情况:它会优先通过指针+大小的路径完成构造,而非卡严格匹配的数组构造模板。 - 使用
-stdlib=libc++能编译,正是因为libc++的std::span实现没有这个限制,其构造逻辑更贴合标准对隐式转换的规则。
内容的提问来源于stack exchange,提问作者Jan Schultke
相关产品推荐
相关产品推荐

