为什么逻辑常量只能添加到 const 指针的 std::span 中?

Mar*_*rkB 1 c++ c++20 std-span

std::span考虑这段代码,它尝试为原始指针向量创建各种对象。

#include <vector>
#include <span>

int main()
{
    struct S {};
    std::vector<S*> v;
    std::span<S*> span1{v};
    std::span<S* const> span2{v};
    std::span<const S* const> span3{v};
    std::span<const S*> span4{v};
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

span3编译正常,但span4失败并出现以下错误:

<source>: In function 'int main()':
<source>:58:32: error: no matching function for call to 'std::span<const main()::S*>::span(<brace-enclosed initializer list>)'
   58 |     std::span<const S*> span4{v};
      |                                ^
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:45,
                 from <source>:5:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/span:231:9: note: candidate: 'template<class _OType, long unsigned int _OExtent>  requires (_Extent == std::dynamic_extent || _OExtent == std::dynamic_extent || _Extent == _OExtent) && (std::__is_array_convertible<_Type, _Tp>::value) constexpr std::span<_Type, _Extent>::span(const std::span<_OType, _OExtent>&) [with long unsigned int _OExtent = _OType; _Type = const main()::S*; long unsigned int _Extent = 18446744073709551615]'
Run Code Online (Sandbox Code Playgroud)

我要么期望span3两者span4都失败,要么两者都成功。有人可以解释为什么逻辑常量可以添加到std::span原始指针当且仅当底层指针是const(即按位)时。

cpp*_*ner 8

  1. std::span<const S*>允许您将 a 分配const S*给一个元素。
  2. std::vector<S*>允许您读取类型的元素S*
  3. 如果std::span<const S*>允许采用 a std::vector<S*>,那么可以通过将 a 分配给跨度的一个元素,然后通过向量读取相同的元素来偷偷地将 a 转换const S*为 a 。S*const S*

也就是说,如果std::span<const S*> span4{v};允许,那么下面的程序将是有效的:

#include <vector>
#include <span>

int main()
{
    struct S { int value; };
    std::vector<S*> v = {nullptr};
    std::span<const S*> span4{v}; // Note: not allowed in reality

    const S s{.value = 0};

    span4[0] = &s;
    S* p = v[0];
    assert(p == &s);
    p->value = 42; // Oops, modifies the value of a const object!
}
Run Code Online (Sandbox Code Playgroud)

因此,为了防止这种情况并提供常量正确性,std::span<const S*>一定不能从std::vector<S*>.

另一方面,std::span<const S* const>不允许您分配给它的元素,因此它采用std::vector<S*>.

(是的,这与您可以将 a 转换S**const S* const *,但不能将其转换为 的原因相同const S**。)