4

Consider the following code:

#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 accept it, gcc rejects it with:

<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);
      |                                            ^

Why does r is not a constant expression?

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • clang also doesn't like the code, with a slightly different take. It seems to be complaining that a is not an array? I have no idea what's going on there. –  Mar 25 '21 at 07:54
  • @dratenik clang has not implemented the [P0896R4](https://en.cppreference.com/w/cpp/compiler_support) yet. – 康桓瑋 Mar 25 '21 at 07:57

1 Answers1

5

The ranges bit is a red herring. It can be boiled down to this

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

We cannot form a constexpr pointer to the first element of a. Such pointers are constrained to hold only the addresses of objects with static storage duration. The view object (as part of its implementation somewhere deep down) is going to need to keep a copy of an iterator (the pointer in our case).

Since r is declared constexpr and it internally holds a pointer to an object, that object too must have static storage duration. Indeed, modifying the code to contain

static constexpr int a[] = {1, 2, 3, 4};

Makes GCC accept your example happily.

MSVC is non-conformant however. It accepts the invalid plain pointer example as well.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • "Such pointers are constrained to hold only the addresses of objects with static storage duration" Could you elaborate on why this is required, even when the object is a constexpr with the same scope? – Karl Knechtel Mar 25 '21 at 08:08
  • @KarlKnechtel - The object being `constexpr` only makes it usable in a constant expression. It still has automatic storage duration, and if it's ODR used the address would be "on the stack". Counter-intuitive maybe, but there you have it. For a pointer to be constexpr, it must hold a value that's "known" even in the abstract machine. Only the addresses of static objects do not "shift". – StoryTeller - Unslander Monica Mar 25 '21 at 08:12
  • 1
    Oh, I see what you mean. Because `a` would be on the stack, its address can't be determined at compile time. – Karl Knechtel Mar 25 '21 at 08:17