c++ variable initialization in ranged loops with mutidimentional arrays - c++

I am learning c++ for the first time(I am transitioning from python)
I see some weird behavior when I try to work with and compile ranged loops using multidimensional arrays. Consider the following case:
#include <iostream>
#include <typeinfo>
int array[2][3]
for (dataType row : array) { std::cout << typeid(row).name(); }
If dataType is const int* row, row becomes a pointer (whose elements) cannot be modified. This is expected.
for (const int* row : array) { std::cout << typeid(row).name(); }
output : int const * __ptr64
If dataType is const auto row, row becomes a pointer that CAN be modified. Regardless of whether or not you try to modify the row inside the code, the compiler ignores your request to make row a constant variable.
for (const auto row : array) { std::cout << typeid(row).name(); }
output : int * __ptr64
In the above case, I can modify the contents of row without any errors.
If you add the asterisk after auto, you will now get a constant variable. You need to both, type const, and put the asterisk to make it constant.
for (const auto* row : array) { std::cout << typeid(row).name(); }
output : int const * __ptr64
Now, if we put the & operator instead of an asterisk, row will become an array of size 3. Here, & isn't modifying the address or anything, I cannot find a way to force the compiler to copy the inner array and put it inside row at a new address. This just makes it easier to work with nested ranged loops.
for (auto& row : array) { std::cout << typeid(row).name(); }
output : int [3]
But now, I cannot manually tell the compiler to make an array. Typing in something like int row[3] won't even get compiled. The only way to get an array is to use auto&
for (int row[3] : array) { std::cout << typeid(row).name(); }
CompilerError E0144: a value of type "int*" cannot be used to initialize an entity of type "int[3]"
for (int& row[3] : array) { cout << typeid(n).name(); }
CompilerError E0144: a value of type "int*" cannot be used to initialize an entity of type "int[3]"
CompilerError E0251: array of reference is not allowed
I like to use nested ranged loops in my code, its a habit I developed while using python. It makes the code much easier to write, read and it's much less error prone. For the sake of readability and debugging, I want a way to force my compiler to initialize row as an array instead of using auto& and letting the compiler decide what dataType it wants for the array.
Also, I want a way to get a deep copy of row at another address so I can modify the contents inside my loop without making changes to the original array. For one dimensional loops, omitting the & operator will make a copy of the data at a different address, but with multidimensional arrays, its always the same address.
It would also be nice to know what is going on when you initialize with const auto as opposed to const auto*. What does adding the asterisk do that is so important for the compiler?

C-arrays are not copyable and have strange/ugly syntax for reference type.
I suggest to use std::array which is a good replacement.
std::array<std::array<int, 3>, 2> array;
for (/*const*/ std::array<int, 3> /*&*/ row : array) {
for (int /*&*/ e : row) {
// ...
}
}
for (/*const*/ auto /*&*/ row : array) {
for (auto /*&*/ e : row) {
// ...
}
}
C-array only allow pass by reference (else decays to pointer)
int array[2][3];
for (/*const*/ int (&row)[3] : array) {
for (int /*&*/ e : row) {
// ...
}
}
for (/*const*/ auto & row : array) {
for (auto /*&*/ e : row) {
// ...
}
}

Credit to M.M:
for (const auto row : array) { std::cout << typeid(row).name(); }
auto here becomes a pointer, then const is applied to it. It is a pointer, the address stored in the pointer cannot change but the value it points to can be changed.
It is the same as writing:
int* const row
conversely const auto* simply becomes const int* as expected.
The compiler reads left to right so here const int* is equivalent to (const int)* which creates a pointer to the datatype const int whereas int* const creates a pointer then applies const to that pointer.
Credit to Igor Tandetnik:
To reference a sub array you need to write the following:
for (int (&row)[3] : array) {}
For a multidimensional arrays, the general syntax would be :
dataType array[a][b][c][d](...) ;
for (dataType (&row)[b][c][d](...) : array) {}
To copy an array, you can use a pointer and do it manually with a for loop. Depending on what you're doing, you should choose whether or not you want to allocate it on the heap. Alternatively, there are c++ libraries that give you copy() functions.

Related

Passing std::vector::data to function expecting type** (double pointer)

As the title describes, I am trying to pass the pointer to the data of a std::vector into a function expecting a double pointer. Take as an example the code below. I have an int pointer d which is passed to myfunc1 as &d (still not sure if call it the pointer's reference or what), where the function changes its reference to the beginning of an int array filled with 1,2,3,4. However, if I have a std::vector of ints and try to pass &(vec.data()) to myfunc1 the compiler throws the error lvalue required as unary ‘&’ operand. I have already tried something like (int *)&(vec.data()) as per this answer, but it does not work.
Just for reference, I know I can do something like myfunc2 where I directly pass the vector as reference and the job is done. But I want to know if it's possible to use myfunc1 with the std::vector's pointer.
Any help will be very much appreciated.
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
void myfunc1(int** ptr)
{
int* values = new int[4];
// Fill all the with data
for(auto& i:{0,1,2,3})
{
values[i] = i+1;
}
*ptr = values;
}
void myfunc2(vector<int> &vec)
{
int* values = new int[4];
// Fill all the with data
for(auto& i:{0,1,2,3})
{
values[i] = i+1;
}
vec.assign(values,values+4);
delete values;
}
int main()
{
// Create int pointer
int* d;
// This works. Reference of d pointing to the array
myfunc1(&d);
// Print values
for(auto& i:{0,1,2,3})
{
cout << d[i] << " ";
}
cout << endl;
// Creates the vector
vector<int> vec;
// This works. Data pointer of std::vector pointing to the array
myfunc2(vec);
// Print values
for (const auto &element : vec) cout << element << " ";
cout << endl;
// This does not work
vector<int> vec2;
vec2.resize(4);
myfunc1(&(vec2.data()));
// Print values
for (const auto &element : vec2) cout << element << " ";
cout << endl;
return 0;
}
EDIT: What my actual code does is to read some binary files from disk, and load parts of the buffer into the vector. I was having troubles getting the modified vector out of a read function, and this is what I came up with that allowed me to solve it.
When you write:
myfunc1(&(vec2.data()));
You are getting the address of a rvalue. The pointed int* is so a temporary that is destroyed right after the call.
This is why you get this error.
But, as #molbdnilo said, in your myfunc1() function, you are reassigning the pointer (without caring to destroy previously allocated memory by the way).
But the std::vector already manages its data memory on its own. You cannot and you must not put your hands on it.
What my actual code does is to read some binary files from disk, and load parts of the buffer into the vector.
A solution could be to construct your std::vector by passing the iterator to the beginning and the iterator to the end of the desired part to extract in the constructor's parameters.
For example:
int * buffer = readAll("path/to/my/file"); // Let's assume the readAll() function exists for this example
// If you want to extract from element 5 to element 9 of the buffer
std::vector<int> vec(buffer+5, buffer+9);
If the std::vector already exists, you can use the assign() member function as you already did in myfunc2():
vec.assign(buffer+5, buffer+9);
Of course in both cases, you have to ensure that you are not trying to access an out of bounds element when accessing the buffer.
The problem is that you cannot take the address of data(), since it is only a temporary copy of the pointer, so writing to a pointer to it makes not that much sense. And that is good that way. You DO NOT want to pass data() to this function since it would overwrite the pointer with a new array and that would break the vector. You can remove one * from the function and only assign to it and not allocate the memory there. This will work, but make sure to allocate the memory in the caller (with resize, just reserve will result un undefined behavior, since data() is only a pointer to the beginning of the valid range [data(), data() + size()). The range [data(), data() + capacity ()) is not necessary valid.

Why isn't this 'for' loop valid?

From C++ Primer 5th Edition by Lippman, page 182, consider:
int ia[3][4];
for (auto row : ia)
for (auto col : row)
The first for iterates through ia, whose elements are arrays of size 4.
Because row is not a reference, when the compiler initializes row it will convert each array element (like any other object of array
type) to a pointer to that array’s first element. As a result, in this
loop the type of row is int*.
I am not really sure that I understand how this auto works, but if I can assume it automatically gives a type to a row based on ia array members type, but I don't understand why this kind of for, where row is not a reference, is not valid. Why is this going to happen? "pointer to that array’s first element", because of what?
The problem is that row is an int * and not a int[4] as one would expect because arrays decay to pointers and there is no automatic way to know how many elements a pointer points to.
To get around that problem std::array has been added where everything works as expected:
#include <array>
int main() {
std::array<std::array<int, 4>, 3> ia;
for (auto &row : ia){
for (auto &col : row){
col = 0;
}
}
}
Note the & before row and col which indicate that you want a reference and not a copy of the rows and columns, otherwise setting col to 0 would have no effect on ia.
To prevent the decay of the int[] to int* you can use &&
int main() {
int ia[3][4];
for (auto && row : ia)
for (auto && col : row)
;
}

Modifying elements of an array through a function

I'm learning about pointers and I can't get this code to work. Here's what I have so far:
void incrementArray(int* a[]) {
for(auto& x : a) {
++x;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int array[] = {0,1,2,3,4,5,6,7,8,9};
for(auto x : array) {
cout << x << '\n';
}
incrementArray(&array);
for(auto x : array) {
cout << x << '\n';
}
}
I'm getting the following error:
'incrementArray' : cannot convert parameter 1 from 'int (*)[10]' to
'int *[]'
What can I do to fix my code?
C-style arrays have funny syntax. To pass the array to a function, use int a[] This does not copy the array and changes to the array inside the function will modify the external array. You only need to call incrementArray(array); no & needed
You could try using std::array class which follows more normal syntax.
you have a pointer as a parameter (a reference to an array), but you wish to modify the actual thing it's pointing to, so you gotta change *a, not a.
You could use an array, vector, list, etc object that would have methods already associated to them that do most of the manipulation you could want
What you are trying to do will not work since the signature of a function taking int a[] as an argument does not contain the necessary length information needed to write a for-each loop (i.e. to instantiate the begin() and end() templates needed to use the for-each syntax). GCC's warning says this fairly clearly:
Error:(14, 19) cannot build range expression with array function parameter 'a' since
parameter with array type 'int *[]' is treated as pointer type 'int **'
I thought this might be do-able with a template, but . . .
EDIT:
It can be done with templates, just took me a moment to wrap my head around the syntax. Here is your example in working condition:
template <size_t N>
void incArray(int (&a)[N]) {
for(auto& x : a) {
++x;
}
}
int main(int argc, const char * argv[])
{
int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto x : array) {
cout << x << " ";
}
cout << endl;
incArray(array);
for (auto x : array) {
cout << x << " ";
}
cout << endl;
return 0;
}
There are a couple approaches you could take to increment the elements of an array, all of which require knowing where to start and where to end. The simple way of doing what you want is to just pass the start and end address pointers, but you could also pass a start address with some offset. Since you are using a C-Style array, your int element has and address int*, so your std::begin(array) is an int* to the first element while std::end(array) points to the address of the location after your last allocated element. In your program, the std::end() address points to the memory location after your 10th allocated element. If you had an array with a size allocation (int other_arr[40]), std::end() will point to the first address after the allocation (std::end(other_arr) would be std::begin(other_arr)+41). C++ has recently introduced non-member std::begin() and std::end() in the <iterator> library, which returns a pointer to the respective element locations in your C-Array.
#include <algorithm> // std::copy
#include <iostream> // std::cout
#include <iterator> // std::begin
void increment_elements(int* begin, const int* end) {
while (begin != end) {
++(*begin);
++begin;
}
}
// An increment functor for std::transform
int increase_element(int i) {
return ++i;
}
int main() {
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (const int x : array) {
std::cout << x << ' ';
}
std::cout << '\n';
increment_elements(std::begin(array), std::end(array));
// Another way to write your print statement above
std::copy(std::begin(array),
std::end(array),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
// Transform array elements through increase_element()
// and print result to cout.
std::transform(std::begin(array),
std::end(array),
std::ostream_iterator<int>(std::cout, " "),
increase_element);
std::cout << '\n';
}
The generalized version of the increment_elements() function can be found in the <algorithm> library as the function std::transform() documented here.
Since you are learning now, here are some habits that you can start to utilize:
Do not use using namespace std; at the global level. By pulling everything in the standard library into the global namespace, you "pollute" it with functionality that can be called if a function call for it exists, since it doesn't require a std:: prefix qualification. Say you were to write a function that calculated the euclidean distance between two (x,y) points, double distance(Point* p1, Point* p2). You decide to use any of the STL containers, such as <vector>. The containers utilize the <iterator> library, which has its own std::distance(T*, T*) function to calculate the distance between two addresses in memory. By bringing std into the global namespace by using namespace std;, you now have 2 functions with the same signature in the same namespace, that do 2 completely different things. This is very bad yet easily avoidable. This general guideline is probably unnecessary for small projects, but I still recommend you just don't ever do it for any project. Ever.
const or const T& your read only operations. When doing operations where you are pulling data for reading and you don't want to modify the data, initialize using const or const T&. const by itself is sufficient for primitive datatypes (int, float, double, char), but non-primitives will require const T& (T is the type). Values that are of type const T& (called const referenced) are read-only (const) and directly accessed (&).

Removing a unique pointer from an array of unique pointers

I'm trying to create a class which maintains a fixed size vector of unique pointers to managed objects, like so:
std::vector<std::unique_ptr<Myclass>> myVector;
The vector gets initialized like so:
myVector.resize(MAX_OBJECTS,nullptr);
Now, what I need to to, is to be able to, on request, remove one of the stored unique pointers without affecting the size of the vector.
I also need to safely add elements to the vector too, without using push_back or emplace_back.
Thanks in advance.
Edit: I want the vector to be of constant size because I want to be able to add and remove elements in constant time.
If you want a vector of fixed size, use std::array.
To remove a unique_ptr in an index, you can use std::unique_ptr::reset():
myVector[i].reset()
To add an element to a specific index (overwriting what was there before) you can use std::unique_ptr::reset() with the new pointer as parameter:
myVector[i].reset(new Myptr(myparameter));
Reading a reference may also help:
http://en.cppreference.com/w/cpp/memory/unique_ptr
http://en.cppreference.com/w/cpp/container/array
http://en.cppreference.com/w/cpp/container/vector
Looks like you want to use a std::array<> rather than forcing std::vector<> to behave like one.
As already pointed out you should use std::array if the size is fixed.
E.g like this:
std::array<std::unique_ptr<YourType>, MAX_OBJECTS> myVector;
You can then remove or add a new pointer like this.
for(auto& v : myVector)
if(v && predicate)
v.reset();// or v.reset(ptr) to set a new one
You can use STL algorithm std::remove, like this:
// all items that should be removed will be the range between removeAt and end(myVector)
auto removeAt = std::remove_if(begin(myVector), end(myVector),
ShouldRemovePredicate);
// reset all items that should be removed to be nullptr
for(auto it = removeAt; it != end(myVector); ++it)
it->reset();
In addition, if the size is known at compile-time I would suggest using std::array<unique_ptr<MyObject>, SIZE> instead of a vector. However, if SIZE is not known at compile-time your code is ok.
You could use std::array instead of a std::vector since you know the number of the elements beforehand and you could add and remove elements like the following example:
#include <iostream>
#include <memory>
#include <array>
class foo {
std::size_t id;
public:
foo() : id(0) {}
foo(std::size_t const _id) : id(_id) {}
std::size_t getid() const { return id; }
};
auto main() ->int {
// construct an array of 3 positions with `nullptr`s
std::array<std::unique_ptr<foo>, 3> arr;
// fill positions
std::unique_ptr<foo> p1(new foo(1));
arr[0] = std::move(p1);
std::unique_ptr<foo> p2(new foo(2));
arr[1] = std::move(p2);
std::unique_ptr<foo> p3(new foo(3));
arr[2] = std::move(p3);
// print array
for(auto &i : arr) if(i != nullptr) std::cout << i->getid() << " ";
std::cout << std::endl;
// reset first position (i.e., remove element at position 0)
arr[0].reset();
// print array
for(auto &i : arr) if(i != nullptr) std::cout << i->getid() << " ";
std::cout << std::endl;
return 0;
}
LIVE DEMO

How does the range-based for work for plain arrays?

In C++11 you can use a range-based for, which acts as the foreach of other languages. It works even with plain C arrays:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
How does it know when to stop? Does it only work with static arrays that have been declared in the same scope the for is used in? How would you use this for with dynamic arrays?
It works for any expression whose type is an array. For example:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
n *= 2;
delete [] arraypointer;
For a more detailed explanation, if the type of the expression passed to the right of : is an array type, then the loop iterates from ptr to ptr + size (ptr pointing to the first element of the array, size being the element count of the array).
This is in contrast to user defined types, which work by looking up begin and end as members if you pass a class object or (if there is no members called that way) non-member functions. Those functions will yield the begin and end iterators (pointing to directly after the last element and the begin of the sequence respectively).
This question clears up why that difference exists.
I think that the most important part of this question is, how C++ knows what the size of an array is (at least I wanted to know it when I found this question).
C++ knows the size of an array, because it's a part of the array's definition - it's the type of the variable. A compiler has to know the type.
Since C++11 std::extent can be used to obtain the size of an array:
int size1{ std::extent< char[5] >::value };
std::cout << "Array size: " << size1 << std::endl;
Of course, this doesn't make much sense, because you have to explicitly provide the size in the first line, which you then obtain in the second line. But you can also use decltype and then it gets more interesting:
char v[] { 'A', 'B', 'C', 'D' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;
According to the latest C++ Working Draft (n3376) the ranged for statement is equivalent to the following:
{
auto && __range = range-init;
for (auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin) {
for-range-declaration = *__begin;
statement
}
}
So it knows how to stop the same way a regular for loop using iterators does.
I think you may be looking for something like the following to provide a way to use the above syntax with arrays which consist of only a pointer and size (dynamic arrays):
template <typename T>
class Range
{
public:
Range(T* collection, size_t size) :
mCollection(collection), mSize(size)
{
}
T* begin() { return &mCollection[0]; }
T* end () { return &mCollection[mSize]; }
private:
T* mCollection;
size_t mSize;
};
This class template can then be used to create a range, over which you can iterate using the new ranged for syntax. I am using this to run through all animation objects in a scene which is imported using a library that only returns a pointer to an array and a size as separate values.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
// Do something with each pAnimation instance here
}
This syntax is, in my opinion, much clearer than what you would get using std::for_each or a plain for loop.
It knows when to stop because it knows the bounds of static arrays.
I'm not sure what do you mean by "dynamic arrays", in any case, if not iterating over static arrays, informally, the compiler looks up the names begin and end in the scope of the class of the object you iterate over, or looks up for begin(range) and end(range) using argument-dependent lookup and uses them as iterators.
For more information, in the C++11 standard (or public draft thereof), "6.5.4 The range-based for statement", pg.145
How does the range-based for work for plain arrays?
Is that to read as, "Tell me what a ranged-for does (with arrays)?"
I'll answer assuming that - Take the following example using nested arrays:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (auto &pl : ia)
Text version:
ia is an array of arrays ("nested array"), containing [3] arrays, with each containing [4] values. The above example loops through ia by it's primary 'range' ([3]), and therefore loops [3] times. Each loop produces one of ia's [3] primary values starting from the first and ending with the last - An array containing [4] values.
First loop: pl equals {1,2,3,4} - An array
Second loop: pl equals {5,6,7,8} - An array
Third loop: pl equals {9,10,11,12} - An array
Before we explain the process, here are some friendly reminders about arrays:
Arrays are interpreted as pointers to their first value - Using an array without any iteration returns the address of the first value
pl must be a reference because we cannot copy arrays
With arrays, when you add a number to the array object itself, it advances forward that many times and 'points' to the equivalent entry - If n is the number in question, then ia[n] is the same as *(ia+n) (We're dereferencing the address that's n entries forward), and ia+n is the same as &ia[n] (We're getting the address of the that entry in the array).
Here's what's going on:
On each loop, pl is set as a reference to ia[n], with n equaling the current loop count starting from 0. So, pl is ia[0] on the first round, on the second it's ia[1], and so on. It retrieves the value via iteration.
The loop goes on so long as ia+n is less than end(ia).
...And that's about it.
It's really just a simplified way to write this:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int n = 0; n != 3; ++n)
auto &pl = ia[n];
If your array isn't nested, then this process becomes a bit simpler in that a reference is not needed, because the iterated value isn't an array but rather a 'normal' value:
int ib[3] = {1,2,3};
// short
for (auto pl : ib)
cout << pl;
// long
for (int n = 0; n != 3; ++n)
cout << ib[n];
Some additional information
What if we didn't want to use the auto keyword when creating pl? What would that look like?
In the following example, pl refers to an array of four integers. On each loop pl is given the value ia[n]:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int (&pl)[4] : ia)
And... That's how it works, with additional information to brush away any confusion. It's just a 'shorthand' for loop that automatically counts for you, but lacks a way to retrieve the current loop without doing it manually.
Some sample code to demonstrate the difference between arrays on Stack vs arrays on Heap
/**
* Question: Can we use range based for built-in arrays
* Answer: Maybe
* 1) Yes, when array is on the Stack
* 2) No, when array is the Heap
* 3) Yes, When the array is on the Stack,
* but the array elements are on the HEAP
*/
void testStackHeapArrays() {
int Size = 5;
Square StackSquares[Size]; // 5 Square's on Stack
int StackInts[Size]; // 5 int's on Stack
// auto is Square, passed as constant reference
for (const auto &Sq : StackSquares)
cout << "StackSquare has length " << Sq.getLength() << endl;
// auto is int, passed as constant reference
// the int values are whatever is in memory!!!
for (const auto &I : StackInts)
cout << "StackInts value is " << I << endl;
// Better version would be: auto HeapSquares = new Square[Size];
Square *HeapSquares = new Square[Size]; // 5 Square's on Heap
int *HeapInts = new int[Size]; // 5 int's on Heap
// does not compile,
// *HeapSquares is a pointer to the start of a memory location,
// compiler cannot know how many Square's it has
// for (auto &Sq : HeapSquares)
// cout << "HeapSquare has length " << Sq.getLength() << endl;
// does not compile, same reason as above
// for (const auto &I : HeapInts)
// cout << "HeapInts value is " << I << endl;
// Create 3 Square objects on the Heap
// Create an array of size-3 on the Stack with Square pointers
// size of array is known to compiler
Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)};
// auto is Square*, passed as constant reference
for (const auto &Sq : HeapSquares2)
cout << "HeapSquare2 has length " << Sq->getLength() << endl;
// Create 3 int objects on the Heap
// Create an array of size-3 on the Stack with int pointers
// size of array is known to compiler
int *HeapInts2[]{new int(23), new int(57), new int(99)};
// auto is int*, passed as constant reference
for (const auto &I : HeapInts2)
cout << "HeapInts2 has value " << *I << endl;
delete[] HeapSquares;
delete[] HeapInts;
for (const auto &Sq : HeapSquares2) delete Sq;
for (const auto &I : HeapInts2) delete I;
// cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack
}