为什么C++20范围适配器的返回视图不是常量表达式?

5

请考虑以下代码:

#include <ranges>

int main() {
  constexpr int a[] = {1, 2, 3, 4};
  constexpr auto r = a | std::views::take(3);
  static_assert(*r.begin() == 1);
}

MSVC接受它,GCC却拒绝了它,显示以下信息:

<source>:5:44: error: 'std::ranges::take_view<std::ranges::ref_view<const int [4]> >{3, std::ranges::ref_view<const int [4]>{(& a)}}' is not a constant expression
    5 |   constexpr auto r = a | std::views::take(3);
      |                                            ^

为什么r不是常量表达式?


clang 也不喜欢这段代码,但稍有不同。它似乎在抱怨 a 不是一个数组?我不知道那里发生了什么。 - user14215102
@dratenik clang目前还没有实现P0896R4 - 康桓瑋
1个回答

5

范围位是一个转移话题。它可以概括为以下:

int main() {
  constexpr int a[] = {1, 2, 3, 4};
  constexpr auto r = a ;
}

我们不能创建一个指向数组a的第一个元素的constexpr指针。这种指针仅限于保存具有静态存储期的对象的地址。视图对象(作为其实现的一部分)将需要保留迭代器的副本(在我们的情况下为指针)。
由于r被声明为constexpr并且它内部持有一个指向对象的指针,那么该对象也必须具有静态存储期。实际上,修改代码如下:
static constexpr int a[] = {1, 2, 3, 4};


这样的指针被限制只能保存具有静态存储期的对象的地址。即使对象是具有相同作用域的constexpr,为什么仍需要这样做?您能详细说明一下吗? - Karl Knechtel
@KarlKnechtel - 对象被声明为constexpr只是使其在常量表达式中可用。它仍具有自动存储期,如果它的ODR被使用,则地址将位于“堆栈”上。这可能与直觉相反,但事实就是如此。要使指针成为constexpr,它必须持有一个在抽象机器中即使“已知”的值。只有静态对象的地址不会“移动”。 - StoryTeller - Unslander Monica
1
哦,我明白你的意思了。因为 a 会在堆栈上,所以它的地址在编译时无法确定。 - Karl Knechtel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接