skip to Main Content

I have a class (struct) that contains a static const std::array private member. I want this member to be static and constant (non-writable). It looks like as if adding the initialization through static function, breaks the constness of the member array.
I would have expected the compiler to complain when I try to write to the array defined as const. Instead, when I run I get:

  1. SEGFAULT when using list initializer,
  2. Ability to write when using said function

Class also provides iterator by just returning std::array iterator. When I initialize std::array through initializer list (outside of struct declaration), then try to modify the element in the array through iterator, I get SEGFAULT, although compiler doesn’t complain (somewhat expected and fine).
However, when I initialize the array through another function (in the code below static std::array<int, 4> HalfCircleStatic_init(); , I am able to change the element value, which is not OK.
Worth mentioning the code below was just to reproduce the issue. I really need the ability to initialize the static const array of greater size in a non-trivial way – i.e. using some trigonometric functions.
I can’t understand why is this happening. Any help or direction appreciated. Thanks.

I tried this:

#include <iostream>
#include <array>

struct ArrayContainer
{
    using iterator = typename std::array<int, 4>::iterator ;

    inline constexpr iterator begin() { return iterator(&arr[0]); }
    inline constexpr iterator end()   { return iterator(&arr[0] + arr.size()); }

private:
    static const std::array<int, 4> arr;

    static std::array<int, 4> HalfCircleStatic_init();
};

const std::array<int, 4> ArrayContainer::arr = {1,2,3,4};

std::array<int, 4> ArrayContainer::HalfCircleStatic_init() {
    std::array<int, 4> retVal{};
    for (int i = 0; i < 4; ++i) retVal[i] = i+1;
    return retVal;
}

int main() {
    ArrayContainer arrCont;
    auto it = arrCont.begin();
    std::cout << "Value at 0: " << *it << std::endl;
    *it = 5;
    std::cout << "Value at 0: " << *it << std::endl;
    return 0;
}

Produces:

Value at 0: 1

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

If I change initializer (definition) to something like:

const std::array<int, 4> ArrayContainer::arr = ArrayContainer::HalfCircleStatic_init();

I get this:

Value at 0: 1
Value at 0: 5

Process finished with exit code 0

Using GCC 8.4 on Ubuntu

3

Answers


  1. Chosen as BEST ANSWER

    OK, seems this might work (in a way). Initialising static const member array using lambda:

    const std::array<int, 4> ArrayContainer::arr = {
            [] {
                std::array<int, 4> retVal{};
                for (int i = 0; i < 4; ++i) retVal[i] = i + 1;
                return retVal;
    
            }() };
    

    This is consistent, in that it produces segfault when I try to write.


  2. You may code like this:

    {
        // In your class
        static constexpr std::array<int, 4> HalfCircleStatic_init();
    }
    
    constexpr std::array<int, 4> ArrayContainer::HalfCircleStatic_init() {
        std::array<int, 4> retVal{};
        for (int i = 0; i < 4; ++i) retVal[i] = i+1;
        return retVal;
    }
    

    The reason why SEGFAULT will be triggered is that you’re writing to the read-only session of the program, and the read-only property is determined before runtime. By providing numbers directly, they’re often hard-coded by the compiler in the program, i.e. your binary program directly contains 1, 2, 3, 4, and when the program is loaded, they will be put on the read-only pages by the OS; but by using a function, the initialization is in fact a runtime thing when there is no optimization, so the compiler has to put your const array to a writable session(else the initialization itself will trigger SEGFAULT, right?). What the code above does is just move the runtime thing to the compilation time(by constexpr), so that the compiler will try to calculate it and then may hard-code it in the binary program. If you want to force the compiler to do it and report an error if it fails, consteval can do that.

    However, for float arrays, compilers are not able to calculate them in the compilation time. Keeping const is the soundest way to protect the read-only data through compilers, and you should not depend on SEGFAULT to debug. For example, you may return a cbegin()-like thing to terminate the writing behavior before running rather than after running.

    Login or Signup to reply.
  3. > inline constexpr iterator begin() { return iterator(&arr[0]); } inline
    > constexpr iterator end()   { return iterator(&arr[0] + arr.size()); }
    

    The above might be an indicator, it should have been possible to return arr.begin(), and arr.end()

    Try const_iterator instead of iterator
    (or make the array member non-const — if you want to be able to modify it after construction)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search