Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr begin of a std::array

I am having trouble understanding why both gcc-8.2.0 and clang-7.0.0 reject the following code (live code here):

#include <array>  int main() {      constexpr std::array<int,3> v{1,2,3};     constexpr auto b = v.begin(); // error: not a constexpr      return 0; } 

with the error

error: '(std::array<int, 3>::const_pointer)(& v.std::array<int,3>::_M_elems)'  is not a constant expression (constexpr auto b = v.begin();) 

According to en.cppreference.com, the begin() member function is declared constexpr. Is this a compiler bug?

like image 203
linuxfever Avatar asked Oct 24 '18 15:10

linuxfever


People also ask

Can std:: array be constexpr?

Since C++17 std::array can be declared as constexpr: constexpr std::array myArray{1, 2, 3}. Now starts the fun part. With C++20, you can use a std::array at compile-time. The std::array (1) and all results of the calculations are declared as constexpr .

How to initialize constexpr array?

Manual initialization A constexpr array can be declared and initialized manually through: constexpr int arr[3] = {1,2,3};

Can std :: string be constexpr?

constexpr started small in C++11 but then, with each Standard revision, improved considerably. In C++20, we can say that there's a culmination point as you can even use std::vector and std::string in constant expressions!


1 Answers

So let's sidestep std::array to make this a bit easier:

template <typename T, size_t N> struct array {     T elems[N];      constexpr T const* begin() const { return elems; } };  void foo() {     constexpr array<int,3> v{{1, 2, 3}};     constexpr auto b = v.begin(); // error }  constexpr array<int, 3> global_v{{1, 2, 3}}; constexpr auto global_b = global_v.begin(); // ok 

Why is b an error but global_b is okay? Likewise, why would b become okay if we declared v to be static constexpr? The problem is fundamentally about pointers. In order to have a constant expression that's a pointer, it has to point to one, known, constant thing, always. That doesn't really work for local variables without static storage duration, since they have fundamentally mutable address. But for function-local statics or globals, they do have one constant address, so you can take a constant pointer to them.


In standardese, from [expr.const]/6:

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

  • if the value is an object of class type, [...]
  • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a function, or a null pointer value, and
  • [...]

b is none of those things in the second bullet, so this fails. But global_b satisfies the bolded condition - as would b if v were declared static.

like image 196
Barry Avatar answered Sep 20 '22 18:09

Barry