Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to fill a 2D array on compile time with a given function. Here is my code:

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 < H; ++i)
      for (int j = 0; j < W; ++j)
        data[i][j] = i * 10 + j;  // This does not work with std::array
  }
};

constexpr Table<3, 5> table;  // I have table.data properly populated at compile time

It works just fine, table.data is properly populated at compile time.

However, if I change plain 2D array int[H][W] with std::array<std::array<int, H>, W>, I have an error in the loop body:

error: call to non-constexpr function 'std::array<_Tp, _Nm>::value_type& std::array<_Tp, _Nm>::operator[](std::array<_Tp, _Nm>::size_type) [with _Tp = int; long unsigned int _Nm = 3ul; std::array<_Tp, _Nm>::reference = int&; std::array<_Tp, _Nm>::value_type = int; std::array<_Tp, _Nm>::size_type = long unsigned int]'
data[i][j] = i * 10 + j;
^
Compilation failed

Obviously, I'm trying to call non-const overload of std::array::operator[], which is not constexpr. The question is, why it is not constexpr? If C++14 allows us to modify variables declared in constexpr scope, why this is not supported by std::array?

I used to think that std::array is just like plain array, only better. But here is an example, where I can use plain array, but cannot use std::array.

like image 770
Mikhail Avatar asked Dec 10 '15 10:12

Mikhail


3 Answers

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

like image 135
Mikhail Avatar answered Oct 22 '22 05:10

Mikhail


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).

like image 10
101010 Avatar answered Oct 22 '22 04:10

101010


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.

like image 6
Richard Hodges Avatar answered Oct 22 '22 04:10

Richard Hodges