0

Say I have struct S:

struct S {
const int i;
const bool b;
}

And I want to create an (non-const) array of this struct like so:

std::array<S, 16> arr;
for(int i = 0; i<arr.size(); i++)
    arr[i] = { i, i%2==0 };

Then the compiler will complain that I didn't initialize the a const variable when I initialized the array.

I tried doing it with a vector as an intermediary. But int the function I'm writing I have to pass the original array in another struct, and return that other struct.

struct OtherStruct {
    const std::array<S,16> sArray;
};

OtherStruct f() {
    std::vector<S> vec(16);
    for(int i = 0; i<16; i++)
        vec.push_back({ i, i%2==0 });
    return { vec.data() };
}

But that didn't work either. I hoped that passing the pointer to the vector data would be cast into a C-style array, from which an std::array could be made. What is the cleanest way to fix this?

I'm working with C++11.

Note that this example is a gross simplification. The line arr[i] = { i, i%2==0 }; would be replaced by a function that parses incoming network data. The actual array is way bigger and the struct is also more complicated. None of the data that will populate the struct will be known at compile time, only the layout.

13
  • Please read about contructor initialization list. You can also define helper function which will be used to initialize field: std::array<S,16> createAlternatingOnesZeros(). Commented Nov 2, 2022 at 9:23
  • @MarekR Yes I have looked into that. But how would I create that initialization_list from my vec? The actual code that I have to instantiate "struct S" is actually done by parsing some raw binary data, so It's a bit more complicated than a single line. The array Is also actually of size 800. So I would really like to have a for loop in here somewhere. Commented Nov 2, 2022 at 9:30
  • 1
    @Typhaon If you need an initializer list like return {vec[0], vec[1], ..., you can create that with BOOST_PP_REPEAT: boost.org/doc/libs/1_77_0/libs/preprocessor/doc/ref/repeat.html Commented Nov 2, 2022 at 9:45
  • 2
    This sounds like an XY problem: const data members is typically an anti-pattern and e.g. the C++ Core Guidelines have a rule against it. It prohibit moves and adds complexity to clients of the type, such as highlighted above. It seems what you are after is logical constness, which can be implemented using a class type instead, with read-only access to its non-const data members. Commented Nov 2, 2022 at 10:02
  • 1
    You still can use placement-new to fill the array items, but I would not recommend it. Usually, it's better to make the members private instead of const (or work with a const S instead). Commented Nov 2, 2022 at 10:10

3 Answers 3

2

You can use parameter pack expansion to create an initialiser list of arbitrary size.

You will need to backport std::index_sequence into C++11

template <size_t... Is>
std::array<S, sizeof...(Is)> make_s_array_impl(index_sequence<Is...>) { 
    return { { { Is, Is % 2 == 0 }... } }; 
}

template <size_t N>
std::array<S, N> make_s_array()
{
    return make_s_array_impl(make_index_sequence<N>{});
}

See it live

Sign up to request clarification or add additional context in comments.

Comments

1

As the number of values is known in the compile time, you can fill the array with an initializer list. You can create it easily with BOOST_PP_REPEAT:

#include <boost/preprocessor/repetition/repeat.hpp>

struct S {
    const int i;
    const bool b;
};

struct OtherStruct {
    const std::array<S,16> sArray;
};

OtherStruct f() {
    #define FILL(z, i, ignored) { i, i%2==0 },

    return {std::array<S,16>{{
        BOOST_PP_REPEAT(16, FILL, ignored)
    }}};

    #undef FILL
}

Comments

-1

In C/C++ when you use the const keyword you cannot left the variable uninitialized when you declare it and neither assign a new value after this is declared.

For example:

const int i = 5;  //this is a correct way to use the const keyword
const int i;
...
i = 5;  //this is NOT  a correct way to use the const keyword

However, you can use const_cast<T> to bind a pointer of the same type to the value of your struct instance and change it only once in the for loop.

In this way you can obtain what you asked.

#include <iostream>
#include <array>


struct S{ 
const bool b = 0; //Intialize the values in order to avoid compiler errors.
const int i = 0;
};
int main(){
    std::array<struct S, 16> arr;
    for(int i = 0; i<arr.size(); i++){
        // declare a variable that point to the const value of your struct instance.
        bool* b = const_cast <bool*> (&arr[i].b);
        int* in  = const_cast <int*> (&arr[i].i);
        *b = i; //change the value with the value you need.
        *in = i%2==0;
            
        }
        
        for(int i = 0; i<arr.size(); i++){

    int a = i%2==0;
    std::cout<< "const bool: " << arr[i].b << " " << (bool)i  << "const int: " << arr[i].i << " " << a << std::endl;
            
        }
    
    
}

2 Comments

This won't work because the compiler will complain at std::array<struct S, 16> arr; Because that's the moment it initializes everything, and const variables don't have a default initialization
More importantly, using const_cast to modify a const object is a constness violation and undefined behaviour. This is NOT the intended use of const_cast. Never ever do this.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.