Sort 2 dimensional c array with std::sort - c++

I cannot seem to sort a 2 dimensional c array with std::sort. I can however sort a one dimensional array. This is in a case where I am being handed a c array in a c++ program and am hoping to sort without copying it over to a std::array. Maybe there is some way to turn it into a std::array without copying it? That sounds doubtful to me as any std::array would then call a destructor on memory that it does not own.
Sorting One dimensional c style array works just fine:
int len = 5;
auto one_dim_less = [](int a, int b){
return a < b;
};
int one_dim[] = {4, 0, 3, 1, 2};
std::sort(one_dim, one_dim + len, one_dim_less);
Attempting to sort two dimensional c style array by second number does not compile:
int len = 5;
auto two_dim_less = [](int a[2], int b[2]){
return a[1] < b[1];
};
int two_dim[][2] = {{1,8}, {2,4}, {3,10}, {4,40}, {5,1}};
std::sort(two_dim, two_dim + len, two_dim_less);

Maybe there is some way to turn it into a std::array without copying
it?
Perhaps not turning into a std::array per se, but an alternative approach might be to cast the 2D C-style arrays into a std::array reference just for the sorting. Doing so in reliance on the standard saying an std::array representation in memory at least begins with its C-style array equivalent. See here under [array.overview§2]:
An array is an aggregate that can be list-initialized with up to N
elements whose types are convertible to T.
In practice, the following usage of reinterpret_cast is most probably safe, but do note that unless there is a special exception for it somewhere in the standard, it would formally be undefined behaviour:
#include <algorithm>
#include <array>
#include <iostream>
int main() {
auto two_dim_less = [](std::array<int, 2>& a, std::array<int, 2>& b) {
return a[1] < b[1]; };
int two_dim[][2] = {{1, 8}, {2, 4}, {3, 10}, {4, 40}, {5, 1}};
std::array<std::array<int, 2>, 5>& arr =
*reinterpret_cast<std::array<std::array<int, 2>, 5>*>(&two_dim);
std::sort(arr.begin(), arr.end(), two_dim_less);
for (int i = 0; i < 5; i++)
std::cout << two_dim[i][0] << ", " << two_dim[i][1] << '\n';
return 0;
}
Output:
5, 1
2, 4
1, 8
3, 10
4, 40
Regarding the use of std::qsort(), note that it is potentially slower than std::sort() due to the latter allowing to inline the comparisons while the former doesn't.

std::sort() requires the objects it's used to sort to be MoveAssginable.
Arrays are not MoveAssginable (nor assignable at all).
Try using an array of structures or std::pairs instead.

Related

C++ Array Reinitialization using Braces [duplicate]

So I am playing around with some arrays, and I cannot figure out why this won't work.
int numbers[5] = {1, 2, 3};
int values[5] = {0, 0, 0, 0, 0};
values = numbers;
The following error appear:
Error 1 error C2106: '=' : left operand must be l-value c:\users\abc\documents\visual studio 2012\projects\consoleapplication7\consoleapplication7\main.cpp 9 1 ConsoleApplication7
Why can't I do like that? What does the error mean?
Arrays have a variety of ugly behavior owing to C++'s backward compatibility with C. One of those behaviors is that arrays are not assignable. Use std::array or std::vector instead.
#include <array>
...
std::array<int,5> numbers = {1,2,3};
std::array<int,5> values = {};
values = numbers;
If, for some reason, you must use arrays, then you will have to copy the elements via a loop, or a function which uses a loop, such as std::copy
#include <algorithm>
...
int numbers[5] = {1, 2, 3};
int values[5] = {};
std::copy(numbers, numbers + 5, values);
As a side note, you may have noticed a difference in the way I initialized the values array, simply providing an empty initializer list. I am relying on a rule from the standard that says that if you provide an initializer list for an aggregate, no matter how partial, all unspecified elements are value initialized. For integer types, value initialization means initialization to zero. So these two are exactly equivalent:
int values[5] = {0, 0, 0, 0, 0};
int values[5] = {};
You can't assign arrays in C++, it's stupid but it's true. You have to copy the array elements one by one. Or you could use a built in function like memcpy or std::copy.
Or you could give up on arrays, and use std::vector instead. They can be assigned.
Array names are constant not modifiable l-value, you can't modify it.
values = numbers;
// ^
// is array name
Read compiler error message: "error C2106: '=' : left operand must be l-value" an l-value is modifiable can be appear at lhs of =.
You can assign array name to a pointer, like:
int* ptr = numbers;
Note: array name is constant but you can modify its content e.g. value[i] = number[i] is an valid expression for 0 <= i < 5.
Why can't I do like that?
Basically this constrain is imposed by language, internally array name uses as base address and by indexing with base address you can access content continue memory allocated for array. So in C/C++ array names with be appear at lhs not a l-value.
values = numbers;
Because numbers and values are pointers.
int numbers[5] = {1, 2, 3};
int values[5] = {0, 0, 0, 0, 0};
for(int i = 0;i < 5; i ++){
values[i] = numbers[i];
}
std::array is a good idea, but this is also possible:
struct arr { int values[5]; };
struct arr a{{1, 2, 3}};
struct arr b{{}};
a = b;
Otherwise, use std::memcpy or std::copy.
I think the reason for non-assignability is that C wants to make sure that any pointers to any element in the array would pretty much always remain valid.
Think about vectors that allow dynamic resizing: the older pointers become invalid (horribly) after resizing happens automatically.
PS. With the design philosophy for vectors, the following block works well.
vector<int> v = {1, 5, 16, 8};
vector<int> v_2 = {7, 5, 16, 8};
v_2 = v;

Sorting 2d array using std sort without vector [duplicate]

I cannot seem to sort a 2 dimensional c array with std::sort. I can however sort a one dimensional array. This is in a case where I am being handed a c array in a c++ program and am hoping to sort without copying it over to a std::array. Maybe there is some way to turn it into a std::array without copying it? That sounds doubtful to me as any std::array would then call a destructor on memory that it does not own.
Sorting One dimensional c style array works just fine:
int len = 5;
auto one_dim_less = [](int a, int b){
return a < b;
};
int one_dim[] = {4, 0, 3, 1, 2};
std::sort(one_dim, one_dim + len, one_dim_less);
Attempting to sort two dimensional c style array by second number does not compile:
int len = 5;
auto two_dim_less = [](int a[2], int b[2]){
return a[1] < b[1];
};
int two_dim[][2] = {{1,8}, {2,4}, {3,10}, {4,40}, {5,1}};
std::sort(two_dim, two_dim + len, two_dim_less);
Maybe there is some way to turn it into a std::array without copying
it?
Perhaps not turning into a std::array per se, but an alternative approach might be to cast the 2D C-style arrays into a std::array reference just for the sorting. Doing so in reliance on the standard saying an std::array representation in memory at least begins with its C-style array equivalent. See here under [array.overview§2]:
An array is an aggregate that can be list-initialized with up to N
elements whose types are convertible to T.
In practice, the following usage of reinterpret_cast is most probably safe, but do note that unless there is a special exception for it somewhere in the standard, it would formally be undefined behaviour:
#include <algorithm>
#include <array>
#include <iostream>
int main() {
auto two_dim_less = [](std::array<int, 2>& a, std::array<int, 2>& b) {
return a[1] < b[1]; };
int two_dim[][2] = {{1, 8}, {2, 4}, {3, 10}, {4, 40}, {5, 1}};
std::array<std::array<int, 2>, 5>& arr =
*reinterpret_cast<std::array<std::array<int, 2>, 5>*>(&two_dim);
std::sort(arr.begin(), arr.end(), two_dim_less);
for (int i = 0; i < 5; i++)
std::cout << two_dim[i][0] << ", " << two_dim[i][1] << '\n';
return 0;
}
Output:
5, 1
2, 4
1, 8
3, 10
4, 40
Regarding the use of std::qsort(), note that it is potentially slower than std::sort() due to the latter allowing to inline the comparisons while the former doesn't.
std::sort() requires the objects it's used to sort to be MoveAssginable.
Arrays are not MoveAssginable (nor assignable at all).
Try using an array of structures or std::pairs instead.

Set array1 = array2 in C++

I have one initialized array arr1 and one declared array arr2. How can I simply set arr2 = arr1?
void setup() {
int arr1[5][2]= { {1,1},
{1,2}};
int arr2[5][2];
arr2 = arr1; // Throws error "invalid array assignment"
}
Is it possible to do that in C++? And if so, how? I'd like to prevent using loops for this.
Arrays can't be assigned, but they can be copied (using e.g. std::copy or std::memcpy).
A possible better solution is to use std::array instead, as then you can use plain and simple assignment:
std::array<std::array<int, 2>, 5> arr1 = {{
{ 1, 1 },
{ 1, 2 }
}};
std::array<std::array<int, 2>, 5> arr2;
arr2 = arr1;
You can’t assign C arrays in this way, but you can assign std::arrays and std::vectors:
auto a1 = std::vector<std::vector<int>>{{1, 1}, {1, 2}};
auto a2 = a1;
(std::arrays work the same way but are more verbose, since you need to specify the number of dimensions as template arguments.)
This example performs copy construction rather than assignment, which is what you’ll want to use 99% of the time. Assignment also works, the same way.
It is worth noting that this is not a multi-dimensional array — it’s a nested array. C++ has no native type for multi-dimensional arrays, but various libraries (mostly for numerical computation) provide them, for instance Eigen and xtensor. These may seem superficially similar to nested arrays, but both their API and their implementation differ in crucial ways. Notably, they are laid out contiguously in memory, which nested vectors aren’t (though nested std::arrays are).
Use std::array:
void setup() {
std::array<std::array<int, 2>, 2> arr1= { {1,1},
{1,2}};
std::array<std::array<int, 2>, 2> arr2;
arr2 = arr1;
}
The easiest solution is to use C++, not C.
std::array<std::array<int,5>,5> arr1 =
{ {1,1},
{1,2} };
auto arr2 = arr1;
Use std::array instead of raw C arrays: it's a POD and can be copied (copy assignment; overwrites every element of the array with the corresponding element of another array) in a natural manner:
#include <array>
#include <iostream>
int main() {
using ArrayType = std::array<std::array<int, 2>, 3>;
ArrayType arr{{
{1, 2},
{3, 4},
{5, 6}
}};
ArrayType arr_copy;
// Copy arr into arr_copy.
arr_copy = arr;
// Mutation of original array will not affect the
// elements of the copy.
arr[0][0] = 42;
std::cout << arr_copy[0][0]; // 1 (original value)
return 0;
}

Is there a simple way to convert a 2D/multidimensional array to a vector in c++? [duplicate]

What is the simplest way to convert array to vector?
void test(vector<int> _array)
{
...
}
int x[3]={1, 2, 3};
test(x); // Syntax error.
I want to convert x from int array to vector in simplest way.
Use the vector constructor that takes two iterators, note that pointers are valid iterators, and use the implicit conversion from arrays to pointers:
int x[3] = {1, 2, 3};
std::vector<int> v(x, x + sizeof x / sizeof x[0]);
test(v);
or
test(std::vector<int>(x, x + sizeof x / sizeof x[0]));
where sizeof x / sizeof x[0] is obviously 3 in this context; it's the generic way of getting the number of elements in an array. Note that x + sizeof x / sizeof x[0] points one element beyond the last element.
Personally, I quite like the C++2011 approach because it neither requires you to use sizeof() nor to remember adjusting the array bounds if you ever change the array bounds (and you can define the relevant function in C++2003 if you want, too):
#include <iterator>
#include <vector>
int x[] = { 1, 2, 3, 4, 5 };
std::vector<int> v(std::begin(x), std::end(x));
Obviously, with C++2011 you might want to use initializer lists anyway:
std::vector<int> v({ 1, 2, 3, 4, 5 });
Pointers can be used like any other iterators:
int x[3] = {1, 2, 3};
std::vector<int> v(x, x + 3);
test(v)
You're asking the wrong question here - instead of forcing everything into a vector ask how you can convert test to work with iterators instead of a specific container. You can provide an overload too in order to retain compatibility (and handle other containers at the same time for free):
void test(const std::vector<int>& in) {
// Iterate over vector and do whatever
}
becomes:
template <typename Iterator>
void test(Iterator begin, const Iterator end) {
// Iterate over range and do whatever
}
template <typename Container>
void test(const Container& in) {
test(std::begin(in), std::end(in));
}
Which lets you do:
int x[3]={1, 2, 3};
test(x); // Now correct
(Ideone demo)
One simple way can be the use of assign() function that is pre-defined in vector class.
e.g.
array[5]={1,2,3,4,5};
vector<int> v;
v.assign(array, array+5); // 5 is size of array.
One way can be to use the array's bound in one go like this:
int a[3] = {1, 2, 3};
vector<int> v(a, *(&a+1));

Assign array to array

So I am playing around with some arrays, and I cannot figure out why this won't work.
int numbers[5] = {1, 2, 3};
int values[5] = {0, 0, 0, 0, 0};
values = numbers;
The following error appear:
Error 1 error C2106: '=' : left operand must be l-value c:\users\abc\documents\visual studio 2012\projects\consoleapplication7\consoleapplication7\main.cpp 9 1 ConsoleApplication7
Why can't I do like that? What does the error mean?
Arrays have a variety of ugly behavior owing to C++'s backward compatibility with C. One of those behaviors is that arrays are not assignable. Use std::array or std::vector instead.
#include <array>
...
std::array<int,5> numbers = {1,2,3};
std::array<int,5> values = {};
values = numbers;
If, for some reason, you must use arrays, then you will have to copy the elements via a loop, or a function which uses a loop, such as std::copy
#include <algorithm>
...
int numbers[5] = {1, 2, 3};
int values[5] = {};
std::copy(numbers, numbers + 5, values);
As a side note, you may have noticed a difference in the way I initialized the values array, simply providing an empty initializer list. I am relying on a rule from the standard that says that if you provide an initializer list for an aggregate, no matter how partial, all unspecified elements are value initialized. For integer types, value initialization means initialization to zero. So these two are exactly equivalent:
int values[5] = {0, 0, 0, 0, 0};
int values[5] = {};
You can't assign arrays in C++, it's stupid but it's true. You have to copy the array elements one by one. Or you could use a built in function like memcpy or std::copy.
Or you could give up on arrays, and use std::vector instead. They can be assigned.
Array names are constant not modifiable l-value, you can't modify it.
values = numbers;
// ^
// is array name
Read compiler error message: "error C2106: '=' : left operand must be l-value" an l-value is modifiable can be appear at lhs of =.
You can assign array name to a pointer, like:
int* ptr = numbers;
Note: array name is constant but you can modify its content e.g. value[i] = number[i] is an valid expression for 0 <= i < 5.
Why can't I do like that?
Basically this constrain is imposed by language, internally array name uses as base address and by indexing with base address you can access content continue memory allocated for array. So in C/C++ array names with be appear at lhs not a l-value.
values = numbers;
Because numbers and values are pointers.
int numbers[5] = {1, 2, 3};
int values[5] = {0, 0, 0, 0, 0};
for(int i = 0;i < 5; i ++){
values[i] = numbers[i];
}
std::array is a good idea, but this is also possible:
struct arr { int values[5]; };
struct arr a{{1, 2, 3}};
struct arr b{{}};
a = b;
Otherwise, use std::memcpy or std::copy.
I think the reason for non-assignability is that C wants to make sure that any pointers to any element in the array would pretty much always remain valid.
Think about vectors that allow dynamic resizing: the older pointers become invalid (horribly) after resizing happens automatically.
PS. With the design philosophy for vectors, the following block works well.
vector<int> v = {1, 5, 16, 8};
vector<int> v_2 = {7, 5, 16, 8};
v_2 = v;