15

我一直在玩弄std::span在 Godbolt 上使用 clang trunk 和 libc++ 的最新规范,发现一些构造函数令人困惑。

特别是,我发现来自普通旧数组的构造函数和 astd::array与其他容器不同。

例如,以下代码似乎可以编译:

std::vector<int*> v = {nullptr, nullptr};
std::span<const int* const> s{v};

但是,这不会:

std::array<int*, 2> a = {nullptr, nullptr}; 
std::span<const int* const> s{a};

这似乎与cppreference.com 上描述构造函数的方式一致,我只是在努力理解为什么会这样。任何人都可以解释一下吗?

4

1 回答 1

9

这似乎是一个疏忽。数组构造函数当前指定为:

template<size_t N> constexpr span(array<value_type, N>& arr) noexcept;
template<size_t N> constexpr span(const array<value_type, N>& arr) noexcept;

但可能应该指定为:

template<class T, size_t N>
    requires std::convertible_to<T(*)[], ElementType(*)[]>
  constexpr span(array<T, N>& arr) noexcept;
template<class T, size_t N>
    requires std::convertible_to<const T(*)[], ElementType(*)[]>
  constexpr span(const array<T, N>& arr) noexcept;

这将使您的示例编译,因为这样做是安全的。我提交了一个 LWG 问题。现在是LWG 3255


措辞已经在[span.cons]/11中指定了这个约束:

template<size_t N> constexpr span(element_type (&arr)[N]) noexcept;
template<size_t N> constexpr span(array<value_type, N>& arr) noexcept;
template<size_t N> constexpr span(const array<value_type, N>& arr) noexcept;

约束

  • extent == dynamic_­extent || N == extenttrue, 并且
  • remove_­pointer_­t<decltype(data(arr))>(*)[]可转换为ElementType(*)[]

所以我们已经有了正确的约束。只是data(arr)在任何这些情况下实际上并不依赖,所以约束很容易满足。我们只需要制作这些模板。

于 2019-08-10T18:09:53.990 回答