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

后端 未结 4 505
终归单人心
终归单人心 2020-12-14 16:03

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

template
struct Table
{
  int data[H][W];
  //std:         


        
相关标签:
4条回答
  • 2020-12-14 16:09

    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.

    0 讨论(0)
  • 2020-12-14 16:17

    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

    0 讨论(0)
  • 2020-12-14 16:20

    This question intrigued me so much that I decided to figure out a solution that would allow the array to be initialised at compile time with a function that took x and y as parameters.

    Presumably this could be adapted for any number of dimensions.

    #include <iostream>
    #include <utility>
    
    
    // function object that turns x and y into some output value. this is the primary predicate
    struct init_cell_xy
    {
        constexpr init_cell_xy() = default;
    
        constexpr int operator()(int x, int y) const
        {
            return (1 + x) * (1 + y);
        }
    };
    
    // function object that applies y to a given x
    template<int X = 1>
    struct init_cell_for_x
    {
        constexpr init_cell_for_x() = default;
    
        constexpr int operator()(int y) const
        {
            return _xy(X, y);
        }
    
    private:
        init_cell_xy _xy;
    };
    
    // an array of dimension 1, initialised at compile time
    template<int Extent>
    struct array1
    {
        template<class F, int...Is>
        constexpr array1(F&& f, std::integer_sequence<int, Is...>)
        : _values { f(Is)... }
        {}
    
        template<class F>
        constexpr array1(F&& f = init_cell_for_x<>())
        : array1(std::forward<F>(f), std::make_integer_sequence<int, Extent>())
        {}
    
        constexpr auto begin() const { return std::begin(_values); }
        constexpr auto end() const { return std::end(_values); }
        constexpr auto& operator[](size_t i) const {
            return _values[i];
        }
    
    private:
        int _values[Extent];
    
        friend std::ostream& operator<<(std::ostream& os, const array1& s)
        {
            os << "[";
            auto sep = " ";
            for (const auto& i : s) {
                os << sep << i;
                sep = ", ";
            }
            return os << " ]";
        }
    };
    
    // an array of dimension 2 - initialised at compile time
    template<int XExtent, int YExtent>
    struct array2
    {
        template<int...Is>
        constexpr array2(std::integer_sequence<int, Is...>)
        : _xs { array1<YExtent>(init_cell_for_x<Is>())... }
        {}
    
        constexpr array2()
        : array2(std::make_integer_sequence<int, XExtent>())
        {}
    
        constexpr auto begin() const { return std::begin(_xs); }
        constexpr auto end() const { return std::end(_xs); }
        constexpr auto& operator[](size_t i) const {
            return _xs[i];
        }
    
    private:
        array1<YExtent> _xs[XExtent];
    
        friend std::ostream& operator<<(std::ostream& os, const array2& s)
        {
            os << "[";
            auto sep = " ";
            for (const auto& i : s) {
                os << sep << i;
                sep = ",\n  ";
            }
            return os << " ]";
        }
    
    };
    
    
    
    
    auto main() -> int
    {
        using namespace std;
    
        constexpr array2<6,6> a;
    
        cout << a << endl;
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-14 16:29

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

    0 讨论(0)
提交回复
热议问题