c++ how to initialize const elements of an array - c++

i need a way to initialize const elements of an array for the program i am currently working on.
The problem is that i have to initialize these elements with a function, there is no way to do it like this:
const int array[255] = {1, 1278632, 188, ...};
because its alot of data i have to generate.
What i tried is to memcpy data to the const int's but that can't work and hasn't worked.
const int array[255];
void generateData(){
for(int i = 0; i < 255; i++) {
initializeSomehowTo(5, array[i]);
}
}
I hope you understand what i am trying, sorry if i doubled the question, i must have overlooked it.

How about this?
#include <array>
typedef std::array<int, 255> Array;
const Array array = generateData();
Array generateData(){
Array a;
for(int i = 0; i < a.size(); i++) {
initializeSomehowTo(a[i]);
}
return a;
}

The easiest approach is to get the filled array from a function and use that to initialize your const (or constexpr) object. However, built-in arrays can't be copied but std::array<T, N> be:
std::array<T, 255> array = initializeData();
If you need a built-in array, I can imagine initializing a static member of a class (template, actually) where the index is expanded from indices expanded from an std::make_index_sequence<255> and used as positional argument in the array, i.e., something along these lines:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
int some_function(std::size_t i) { return i; }
template <typename> struct initialized_array_base;
template <std::size_t... I>
struct initialized_array_base<std::index_sequence<I...>> {
static const int array[sizeof...(I)];
};
template <std::size_t... I>
int const initialized_array_base<std::index_sequence<I...>>::array[sizeof...(I)]
= { some_function(I)... };
struct initialized_array
:initialized_array_base<std::make_index_sequence<256>> {
};
int main() {
std::copy(std::begin(initialized_array::array),
std::end(initialized_array::array),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}

You can create a writable array, initialize it, and, then, create a const reference to it.
int arry[255];
void generateData(){
for(int i = 0; i < 255; i++) {
initializeSomehowTo(5, arry[i]);
}
}
const int (&array)[255] = arry;

Related

How do I create an std::array of immutable structs? I.e. structs with only const values [duplicate]

This question already has answers here:
Initialize an std::array algorithmically at compile time
(3 answers)
Populate An Array Using Constexpr at Compile-time
(4 answers)
Initialize array whose size is a compile-time constant to single value
(3 answers)
Closed 4 months ago.
This post was edited and submitted for review 4 months ago and failed to reopen the post:
Original close reason(s) were not resolved
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.
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
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
}
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;
}
}

C2664 cannot convert argument from 'initializer list'

I have the following code that I'm running on Visual Studio 2017. This code is a simple exercise to implement a linear search on an array.
The template is used because the function will be used to any type of array, char array, int array, etc.
#include "stdafx.h"
#include <iostream>
#include <vector>
template <typename T>
int linearSearch(T* arr, int size, T varToSearch) {
for (int i = 0; i < size; i++) {
if (arr[i] == varToSearch) return i;
}
return -1;
}
int main()
{
std::cout << linearSearch({ 'a','b','c','d' }, 4, 'd') << std::endl;
return 0;
}
I get the error of the title and after a long search I did not find the problem.
The microsoft page regarding the error, here, does not have relevant information to understand what is happening.
For me the function should work this way: I have the typename T, that will basically be an int or a char. Let's say it is a char.
When I'm passing {'a','b','c','d'} it will decay into a pointer and, as the type of T is char, I would have following:
int linearSearch(char* arr, int size, char varToSearch)
What for me should work normally.
EDIT
After reading the commentaries and giving a thought about the answers, this is what is happening if you are stuck on this problem also. Let's say you have this syntax in a function:
void exampleFunction(char *text){ \\whatever}
And when using the function you pass this:
exampleFunction({'a', 'b', 'c'}){ \\whatever}
If you are expecting {'a', 'b', 'c'} to decay into a pointer so that you can iterate with text[], it does not. With this syntax you will get an std::initializer_list, and not an array.
You could do the following:
char arr[] = {'a', 'b', 'c'};
exampleFunction(arr){ \\whatever};
This way arr will decay into a pointer.
Regarding the problem in my code, I preferred to use a std::vector.
template <typename T>
int linearSearch(std::vector<T> list, T varToSearch) {
for (typename std::vector<T>::iterator it = list.begin(); it != list.end(); it++) {
if (varToSearch == *it) return (it - list.begin());
}
return -1;
}
Because you can't create array this way. This thing { 'a','b','c','d' } called initializer list, but it doesn't supported operator overload. So this you have 2 solution:
First create array before you called function.
Or you can change function declaration to accepting std::vector by value,and send them initializer list this should works.
And sorry for my engilsh.
as others mentioned you can not do that. you can use the vectors but for some reason if you can't, you can try c arrays or perhaps a better alternative std::array instead.
#include <iostream>
#include <array>
template <typename T, size_t N>
int linearSearch(std::array<T, N> & arr, T varToSearch)
{
int i = 0;
for(auto& element : arr)//iterating through each element
{
if (element == varToSearch)
return i;
++i;
}
return -1;
}
int main()
{
std::array<char, 4> arr1 = {'a','b','c','d'};
std::cout << linearSearch(arr1,'d') << std::endl;
return 0;
}

Template function does not work with dynamic size array [duplicate]

I have the following code which could not be complied.
using namespace std;
void f(int);
template<typename T1, size_t N>
void array_ini_1d(T1 (&x)[N])
{
for (int i = 0; i < N; i++)
{
x[i] = 0;
}
}
What is the proper way to pass the array if the main is something like below.
int main()
{
int a;
cin >> a;
int n = a / 4;
f(n);
return 0;
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
error: no matching function to call to array_ini_1d..............
The problem is that variable size arrays are not supported by c++, and is only supported as compilers extension. That means, the standard doesn't say what should happen, and you should see if you can find in compiler's documentation, but I doubt that such corner cases are documented.
So, this is the problem :
int arr[n];
The solution is to avoid it, and use something supported by c++, like for example std::vector.
I don't think the compiler can deduce the size of a variable-length array in a template. Also, don't forget to forward declare f before you use it. Variable-length arrays are a GCC extension and you should get a warning regarding their use.
You may declare your function like this:
template <typename A, size_t N> void f(A a[N]) {
for(size_t i = 0; i < N; i++)
cout << a[i];
}
However, the problem is that when you call the function, the compiler won't deduce the template parameters, and you will have to specify them explicitly.
char arr[5] = {'H', 'e', 'l', 'l', 'o'};
int main()
{
//f(arr); //Won't work
f<char, sizeof(arr)/sizeof(arr[0])>(arr);
cout << endl;
return 0;
}
Unfortunately, that ruins the very idea...
UPD: And even that code does NOT work for an array that has variable length, for the length is calculated at runtime, and the template parameters are defined at compilation time.
UPD2: If using std::vector you may create it initialized:
vector<int> arr(n, 0);
Or you may fill it with fill from <algorithm> when needed:
std::fill(arr.begin(), arr.end(), 0);
As you use Variable length array (VLA) (compiler extension), compiler cannot deduce N.
You have to pass it by pointer and give the size:
template<typename T>
void array_ini_1d(T* a, std::size_t n)
{
for (std::size_t i = 0; i != n; ++i) {
a[i] = 0;
}
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
Or use std::vector. (no extension used so). Which seems cleaner:
template<typename T>
void array_ini_1d(std::vector<T>& v)
{
for (std::size_t i = 0, size = v.size(); i != n; ++i) {
a[i] = 0; // or other stuff.
}
}
void f(int n)
{
std::vector<int> arr(n); // or arr(n, 0).
array_ini_1d(arr);
}
Template parameters must be resolved at compile-time.
There is no way that a function template with parameter size_t N can match any sort of array or other container whose size comes from a run-time input.
You will need to provide another version of the array_1d_ini which does not have the size as a template parameter.
template<typename T, size_t N>
void f(T* a)
{
/* add your code here */
}
int main()
{
int a[10];
f<int, 10>(a);
return 0;
}

How to pass a VLA to a function template?

I have the following code which could not be complied.
using namespace std;
void f(int);
template<typename T1, size_t N>
void array_ini_1d(T1 (&x)[N])
{
for (int i = 0; i < N; i++)
{
x[i] = 0;
}
}
What is the proper way to pass the array if the main is something like below.
int main()
{
int a;
cin >> a;
int n = a / 4;
f(n);
return 0;
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
error: no matching function to call to array_ini_1d..............
The problem is that variable size arrays are not supported by c++, and is only supported as compilers extension. That means, the standard doesn't say what should happen, and you should see if you can find in compiler's documentation, but I doubt that such corner cases are documented.
So, this is the problem :
int arr[n];
The solution is to avoid it, and use something supported by c++, like for example std::vector.
I don't think the compiler can deduce the size of a variable-length array in a template. Also, don't forget to forward declare f before you use it. Variable-length arrays are a GCC extension and you should get a warning regarding their use.
You may declare your function like this:
template <typename A, size_t N> void f(A a[N]) {
for(size_t i = 0; i < N; i++)
cout << a[i];
}
However, the problem is that when you call the function, the compiler won't deduce the template parameters, and you will have to specify them explicitly.
char arr[5] = {'H', 'e', 'l', 'l', 'o'};
int main()
{
//f(arr); //Won't work
f<char, sizeof(arr)/sizeof(arr[0])>(arr);
cout << endl;
return 0;
}
Unfortunately, that ruins the very idea...
UPD: And even that code does NOT work for an array that has variable length, for the length is calculated at runtime, and the template parameters are defined at compilation time.
UPD2: If using std::vector you may create it initialized:
vector<int> arr(n, 0);
Or you may fill it with fill from <algorithm> when needed:
std::fill(arr.begin(), arr.end(), 0);
As you use Variable length array (VLA) (compiler extension), compiler cannot deduce N.
You have to pass it by pointer and give the size:
template<typename T>
void array_ini_1d(T* a, std::size_t n)
{
for (std::size_t i = 0; i != n; ++i) {
a[i] = 0;
}
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
Or use std::vector. (no extension used so). Which seems cleaner:
template<typename T>
void array_ini_1d(std::vector<T>& v)
{
for (std::size_t i = 0, size = v.size(); i != n; ++i) {
a[i] = 0; // or other stuff.
}
}
void f(int n)
{
std::vector<int> arr(n); // or arr(n, 0).
array_ini_1d(arr);
}
Template parameters must be resolved at compile-time.
There is no way that a function template with parameter size_t N can match any sort of array or other container whose size comes from a run-time input.
You will need to provide another version of the array_1d_ini which does not have the size as a template parameter.
template<typename T, size_t N>
void f(T* a)
{
/* add your code here */
}
int main()
{
int a[10];
f<int, 10>(a);
return 0;
}

c++ array[var][2] as a class member

I would like to have an array of unsigned integers within a class, and its size should be [var][2], so the user will be able to choose var in runtime.
Is there a better way than allocating a two dimensional array (an allocated array of pointers to allocated arrays)?
In the class I have:
unsigned int *(*hashFunc);
And in the initializing function:
hashFunc = new unsigned int*[var];
for(unsigned int i = 0; i<var; ++i)
hashFunc[i] = new unsigned int[2];
I want to only allocate once, and I think it should somehow be possible because I only have one unknown dimension (var is unknown but 2 I know from the beginning).
Thanks!
If the sizes are known at compilation time, you should use std::array. If one of the dimensions are not known until runtime, you should use std::vector.
You can of course combine them:
std::vector<std::array<unsigned int, 2>> hashFunc;
The above declares hashFunc to be a vector of arrays, and the arrays is of size two and of type unsigned int, just like specified in the question.
Then to add a new inner array just use push_back of the vector:
hashFunc.push_back({{ 1, 2 }});
(And yes, double braces are needed. The outer to construct the std::array object, and the inner for the actual array data.)
Or if you want to set the size of the outer vector at once (for example if you (runtime) know the size beforehand) you could do e.g.
hashFunc = std::vector<std::array<unsigned int, 2>>(var);
Where var above is the size of the "first dimension". Now you can directly access hashFunc[x][y] where x is in range of var and y is zero or one.
(To answer the direct question.) You can declare the pointer as
int (*hashFunc)[2];
and allocate it in one shot as
hashFunc = new int[var][2];
There's two ways you can go about this. Either have a class with a bare pointer or encapsulate it with std::vector and std::array. Below is a sample of two possible implementations which do exactly the same.
#include <iostream>
#include <vector>
#include <array>
#include <stdexcept>
class TheClass {
public:
typedef int TwoInts[2];
TheClass(const std::size_t size) : m_size(size)
{
m_hashFunc = new TwoInts[m_size];
if (m_hashFunc == NULL) throw std::runtime_error("Ran out of memory.");
}
virtual ~TheClass()
{
delete [] m_hashFunc;
}
inline std::size_t size() const { return m_size; }
inline int& operator()(const std::size_t i, const std::size_t j) { return m_hashFunc[i][j]; }
inline const int& operator()(const std::size_t i, const std::size_t j) const { return m_hashFunc[i][j]; }
private:
std::size_t m_size;
TwoInts* m_hashFunc;
};
class AnotherClass {
public:
AnotherClass(const std::size_t size) : m_hashFunc(size)
{
// Nothing to do here.
}
// No destructor required.
inline std::size_t size() const { return m_hashFunc.size(); }
inline int& operator()(const std::size_t i, const std::size_t j) { return m_hashFunc[i][j]; }
inline const int& operator()(const std::size_t i, const std::size_t j) const { return m_hashFunc[i][j]; }
private:
std::vector<std::array<int, 2>> m_hashFunc;
};
int main(int argc, char *argv[]) {
if (argc < 2) return -1;
const std::size_t runtimesize = static_cast<std::size_t>(atoll(argv[1]));
const std::size_t i1 = rand() % runtimesize;
const std::size_t i2 = rand() % runtimesize;
TheClass instance1(runtimesize);
AnotherClass instance2(runtimesize);
instance1(i1,0) = instance2(i1,0) = 4;
instance1(i2,1) = instance2(i2,1) = 2;
std::cout << instance1(i1,0) << ' ' << instance2(i1,0) << std::endl;
std::cout << instance1(i2,1) << ' ' << instance2(i2,1) << std::endl;
std::cout << instance1.size() << std::endl;
std::cout << instance2.size() << std::endl;
// ... etc
return 0;
}