You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:20:23