Why is non-const std::array::operator[] not constexpr?

Ok, it is indeed an oversight in the standard. There even exists a proposal to fix this: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0107r0.pdf

[N3598] removed the implicit marking of constexpr member functions as const. However, the member functions of std::array were not revisited after this change, leading to a surprising lack of support for constexpr in std::array’s interface. This paper fixes this omission by adding constexpr to the member functions of std::array that can support it with a minimal amount of work.

UPD: Fixed in C++17: https://en.cppreference.com/w/cpp/container/array/operator_at


std::array::operator[] since C++14 is constexpr but is also const qualified:

constexpr const_reference operator[]( size_type pos ) const;
                                                      ^^^^^

Thus you have to cast the arrays to invoke the correct operator[] overload:

template<int H, int W>
struct Table
{
  //int data[H][W];
  std::array<std::array<int, H>, W> data;  // This does not work

  constexpr Table() : data{} {
    for (int i = 0; i < W; ++i)
      for (int j = 0; j < H; ++j)
        const_cast<int&>(static_cast<std::array<int, H> const&>(static_cast<std::array<std::array<int, H>, W> const&>(data)[i])[j]) = 10 + j;
  }
};

Live Demo

Edit:

As opposed by some people, use of const_cast in such a way does not imply undefined behaviour. In fact as proposed in the proposals for the relaxation of constexpr, it is required by the users to do this work around with const_cast in order to evoke the correct subscript operator overload at least until the issue is resolved in C++17 (see link).


While my first thought was "why would you need a constexpr method on a non-const array"? ...

I then sat down and wrote a little test to see if the idea made sense:

#include <iostream>

using namespace std;
struct X{

    constexpr X()
    : _p { 0, 1, 2, 3, 4, 5, 6, 7, 9 }
    {
    }

    constexpr int& operator[](size_t i)
    {
        return _p[i];
    }

    int _p[10];
};

constexpr int foo()
{
    X x;
    x[3] = 4;
    return x[3];
}


auto main() -> int
{
    cout << foo() << endl;

    return 0;
}

It turns out that it does.

So I'm drawing the conclusion that the committee took the same "obvious" view that I did and discounted the idea.

Looks to me as if a proposal could be put forward to the committee to change it in c++17 - giving this question as an example.