Foreach loop with multidimensional arrays in c++ - c++

I am getting an error while compiling the following cpp code:
int x[][2]{{1, 2}, {3, 4}};
for (int e[2] : x) {
std::cout << e[0] << ' ' << e[1] << '\n';
}
This gives the following error:
error: array must be initialized with a brace-enclosed initializer
I did replaced int e[2] with auto e and that worked but I want to work with the actual type.
Is there any workaround?

the correct fixed-size declaration is
for (int(&e)[2] : x) {}
or you can use auto& to deduce it
for (auto& e : x) {} // same as above
note: auto doesn't deduce the same type
for (auto e : x) {} // e is int*

interpret the inner array as a pointer:
#include <iostream>
int main() {
int x[][2]{{1, 2}, {3, 4}};
for (int* e : x) {
std::cout << e[0] << ' ' << e[1] << '\n';
}
}

Related

Create inline multidimensional arrays in C++

I have the following program, which I'd like to change to declare the multidimensional array inline:
int main() {
int x[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
for (const auto& [y, z] : x) {
std::cout << y << ", " << z << std::endl;
}
}
Is there a way to create inline arrays? Something like this:
for (const auto& [y, z] : <magic>{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}) {
std::cout << y << ", " << z << std::endl;
}
Not a very nice way, here is a possibility though:
for (const auto& [y, z] : std::array<std::pair<int, int>, 4>{{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}}) {
std::cout << y << ", " << z << std::endl;
}
One dimensional arrays like can be done using template type deduction with more modern versions of c++, which is a bit cleaner:
for (const auto& x : std::array{0, 1, 2}) {
std::cout << x << std::endl;
}
Introducing pairs makes it a fair bit messier though since the deduction doesn't work as nicely as you'd hope.
you can try a wrapper of C-style array to make it:
template<typename T>
struct xarray{
T data;
auto begin(){
using std::begin;
return begin(data);
}
auto end(){
using std::end;
return end(data);
}
};
template<typename T, typename... Y>
xarray(T, Y...) -> xarray<T[sizeof...(Y) + 1]>;
template<typename T, size_t X>
xarray(T (&&)[X]) -> xarray<T[X]>;
template<typename T, size_t X, size_t Y>
xarray(T (&&)[X][Y]) -> xarray<T[X][Y]>;
// ...
int main(){
for (auto&& [x, y] : xarray{{ {0, 1}, {0, 2} }}){ // additional {} is needed.
std::cout << x << " " << y << std::endl;
}
}
it's exactly equal to your code by C-style array.

Sort just one member of the classes in a vector, leaving the other members unchanged

There are tons of answers for sorting a vector of struct in regards to a member variable. That is easy with std::sort and a predicate function, comparing the structs member. Really easy.
But I have a different question. Assume that I have the following struct:
struct Test {
int a{};
int b{};
int toSort{};
};
and a vector of that struct, like for example:
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
I do not want to sort the vectors elements, but only the values in the member variable. So the expected output should be equal to:
std::vector<Test> tvSorted{ {1,1,5},{2,2,6},{3,3,7},{4,4,8},{5,5,9} };
I wanted to have the solution to be somehow a generic solution. Then I came up with a (sorry for that) preprocessor-macro-solution. Please see the following example code:
#include <iostream>
#include <vector>
#include <algorithm>
struct Test {
int a{};
int b{};
int toSort{};
};
#define SortSpecial(vec,Struct,Member) \
do { \
std::vector<decltype(Struct::Member)> vt{}; \
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [](const Struct& s) {return s.Member; }); \
std::sort(vt.begin(), vt.end()); \
std::for_each(vec.begin(), vec.end(), [&vt, i = 0U](Struct & s) mutable {s.Member = vt[i++]; }); \
} while (false)
int main()
{
// Define a vector of struct Test
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
// Call sort macro
SortSpecial(tv, Test, toSort);
std::cout << "\n\nSorted\n";
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
Since macros shouldn't be used in C++, here my questions:
1. Is a solution with the algorithm library possible?
2. Or can this be achieved via templates?
To translate your current solution to a template solution is fairly straight forward.
template <typename T, typename ValueType>
void SpecialSort(std::vector<T>& vec, ValueType T::* mPtr) {
std::vector<ValueType> vt;
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [&](const T& s) {return s.*mPtr; });
std::sort(vt.begin(), vt.end());
std::for_each(vec.begin(), vec.end(), [&, i = 0U](T& s) mutable {s.*mPtr = vt[i++]; });
}
And we can call it by passing in the vector and a pointer-to-member.
SpecialSort(tv, &Test::toSort);
Somewhow like this (You just need to duplicate, rename and edit the "switchToShort" funtion for the rest of the variables if you want):
#include <iostream>
#include <vector>
struct Test {
int a{};
int b{};
int toSort{};
};
void switchToShort(Test &a, Test &b) {
if (a.toSort > b.toSort) {
int temp = a.toSort;
a.toSort = b.toSort;
b.toSort = temp;
}
}
//void switchToA(Test& a, Test& b) { ... }
//void switchToB(Test& a, Test& b) { ... }
inline void sortMemeberValues(std::vector<Test>& data, void (*funct)(Test&, Test&)) {
for (int i = 0; i < data.size(); i++) {
for (int j = i + 1; j < data.size(); j++) {
(*funct)(data[i], data[j]);
}
}
}
int main() {
std::vector<Test> tv { { 1, 1, 9 }, { 2, 2, 8 }, { 3,3 ,7 }, { 4, 4, 6 }, { 5, 5, 5} };
sortMemeberValues(tv, switchToShort);
//sortMemeberValues(tv, switchToA);
//sortMemeberValues(tv, switchToB);
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
With range-v3 (and soon ranges in C++20), you might simply do:
auto r = tv | ranges::view::transform(&Test::toSort);
std::sort(r.begin(), r.end());
Demo

array initialization requires a brace-enclosed initializer list lambda

I'm new to lambda expression and little confused why I'm getting the error here?
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int arr[] = { 11, 21, 4, 13 };
for_each(arr, arr + 4, [arr](int x) {
cout << x;
});
return 0;
}
I just add LAMBDA for this function.
void fun1(int x)
{
cout << x << " ";
}
Here is the error message on visual studio.
'main::<lambda_4ee0815d3a456ed46cc70a2a94c10f76>::arr':
array initialization requires a brace-enclosed initializer list Project1
You cannot copy arrays, so you can capture arr by reference instead if you actually need it:
for_each(arr, arr + 4, [&arr](int x) { cout << x; });
// ^^^
However, since you don't refer to the array in the lambda body, you don't need to capture it at all:
for_each(arr, arr + 4, [](int x) { cout << x; });
// ^^^^

How to properly initialize a boost multi_array of objects?

I have been surprised to find that boost::multi_array seems to allocate its initial elements differently from, say, std::vector. It does not seem to fill each element with a unique element (using its default value or default constructor). I'm having trouble finding more information about this.
Is there a way to make the multi_array fill itself with a unique object at each element?
For example, consider the following:
static int num = 0;
struct A {
int n;
A() : n((::num)++) {
std::cout << "A()" << std::endl;
}
virtual ~A() {}
void print() {
std::cout << "n=" << n << std::endl;
}
};
int main() {
std::cout << "vector:" << std::endl;
std::vector<A> v(3);
for (auto x : v) {
x.print();
}
std::cout << "multi:" << std::endl;
boost::multi_array<A, 2> m(boost::extents[2][2]);
for (auto x : m) {
for (auto y : x) {
y.print();
}
}
}
This results in the output:
vector:
A()
A()
A()
n=0
n=1
n=2
multi:
A()
n=3
n=3
n=3
n=3
Why is the constructor called only once for the multi_array? How can the multi_array be "filled out" with unique objects (using A's default constructor)?
To quickly fill the whole array do something like fill_n¹:
std::fill_n(a.data(), a.num_elements(), 0);
With boost multi_array you can use a view to your own memory buffer to get the same performance (std::uninitialized_copy is your friend). (actually, you could even map an array view on existing memory, and you want to keep the existing values).
I've written a comparative demo about this here: pointers to a class in dynamically allocated boost multi_array, not compiling
Live On Coliru
#include <boost/multi_array.hpp>
#include <type_traits>
#include <memory>
struct octreenode { int a; int b; };
class world {
public:
world(double x, double y, double z, int widtheast, int widthnorth, int height)
:
originx(x), originy(y), originz(z),
chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height)
{
#define OPTION 4
#if OPTION == 1
static_assert(std::is_trivially_destructible<octreenode>::value, "assumption made");
//std::uninitialized_fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
std::fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
#elif OPTION == 2
for(auto a:chunk) for(auto b:a) for(auto&c:b) c = octreenode{1, 72};
#elif OPTION == 3
for (index cz = 0; cz < chunksnorth; ++cz) {
for (index cx = 0; cx < chunkseast; ++cx) {
for (index cy = 0; cy < chunksup; ++cy) {
chunk[cz][cx][cy] = octreenode{1, 72};
}
}
}
#elif OPTION == 4
static_assert(std::is_trivially_destructible<octreenode>::value, "assumption made");
for (index cz = 0; cz < chunksnorth; ++cz) {
for (index cx = 0; cx < chunkseast; ++cx) {
for (index cy = 0; cy < chunksup; ++cy) {
new (&chunk[cz][cx][cy]) octreenode{1, 72};
}
}
}
#endif
(void) originx, (void) originy, (void) originz, (void) chunksup, (void) chunkseast, (void) chunksnorth;
}
private:
double originx, originy, originz;
int chunkseast, chunksnorth, chunksup;
#if 1
typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
typedef planetchunkarray::index index;
planetchunkarray chunk{boost::extents[chunksnorth][chunkseast][chunksup]};
#else
static_assert(boost::is_trivially_destructible<octreenode>::value, "assumption made");
std::unique_ptr<octreenode[]> raw { new octreenode[chunksnorth*chunkseast*chunksup] };
typedef boost::multi_array_ref<octreenode, 3> planetchunkarray;
typedef planetchunkarray::index index;
planetchunkarray chunk{raw.get(), boost::extents[chunksnorth][chunkseast][chunksup]};
#endif
};
int main() {
world w(1,2,3,4,5,6);
}
The variant using multi_array_ref is an example of how to avoid copy-constructing the elements (it's akin to the optimization used by std::vector when it uses uninitialized memory for reserved but unused elements).
¹ Of course for unique values, use std::iota or std::generate
http://en.cppreference.com/w/cpp/algorithm/iota
http://en.cppreference.com/w/cpp/algorithm/generate_n
So on further study, I learned two things:
boost::multi_array uses the copy constructor to initialize objects into the container, not the default constructor.
The for (auto x : container) way of looping in C++11 seems (at least with clang++ 3.5) to loop over copies of the container elements, rather than iterators (or references).
Modifying the original question's example to demonstrate point 1.
Adding a copy constructor (and corresponding counter), and using auto& x for the object loops rather than auto x:
static int num = 0;
static int cpy = 0;
struct A {
int n;
int c;
A() : n((::num)++), c(0) {
std::cout << "A_def()" << std::endl;
}
A(const A& o) : n(0), c((::cpy)++) {
std::cout << "A_cpy()" << std::endl;
}
virtual ~A() {}
void print() {
std::cout << "n=" << n << ",c=" << c << std::endl;
}
};
int main() {
std::cout << "vector:" << std::endl;
std::vector<A> v(3);
for (auto& x : v) {
x.print();
}
std::cout << "multi:" << std::endl;
boost::multi_array<A, 2> m(boost::extents[2][2]);
for (auto x : m) {
for (auto& y : x) {
y.print();
}
}
}
Produces the output
vector:
A_def() // <- vector allocation starts
A_def()
A_def()
n=0,c=0 // <- vector printing starts, using "for (auto& x)"
n=1,c=0
n=2,c=0
multi:
A_def() // <- a temporary object for multi_array allocation
A_cpy() // <- multi_array allocation starts
A_cpy()
A_cpy()
A_cpy()
n=0,c=0 // <- multi_array prints starts, using "for (auto& y)"
n=0,c=1
n=0,c=2
n=0,c=3
Modifying the example above to demonstrate point 2.
Same class definition as above in this answer, but removing the auto& x from the object loops, and going back to using auto x as done in the original question.
std::cout << "vector:" << std::endl;
std::vector<A> v(3);
for (auto x : v) {
x.print();
}
std::cout << "multi:" << std::endl;
boost::multi_array<A, 2> m(boost::extents[2][2]);
for (auto x : m) {
for (auto y : x) {
y.print();
}
}
Produces output that shows the copy constructor gets called during the print loops, even for elements in the vector.
vector:
A_def() // <- vector allocation starts
A_def()
A_def()
A_cpy() // <- vector printing starts, using "for (auto x)"
n=0,c=0
A_cpy()
n=0,c=1
A_cpy()
n=0,c=2
multi:
A_def() // <- a temporary object for multi_array allocation
A_cpy() // <- multi_array allocation starts
A_cpy()
A_cpy()
A_cpy()
A_cpy() // <- multi_array printing starts, using "for (auto y)"
n=0,c=7
A_cpy()
n=0,c=8
A_cpy()
n=0,c=9
A_cpy()
n=0,c=10

no matching function for call to ‘begin(int**&)’

I wrote a c++ program as fllow(3.43.cpp):
#include <iostream>
using std::cout;
using std::endl;
void version_1(int **arr) {
for (const int (&p)[4] : arr) {
for (int q : p) {
cout << q << " ";
}
cout << endl;
}
}
int main() {
int arr[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
version_1(arr);
return 0;
}
Then I compile it by using: gcc my.cpp -std=c++11, there is an error I can not deal with.
Info:
3.43.cpp:6:30: error: no matching function for call to ‘begin(int**&)’
for (const int (&p)[4] : arr) {
^
3.43.cpp:6:30: note: candidates are:
In file included from /usr/include/c++/4.8.2/bits/basic_string.h:42:0,
from /usr/include/c++/4.8.2/string:52,
from /usr/include/c++/4.8.2/bits/locale_classes.h:40,
from /usr/include/c++/4.8.2/bits/ios_base.h:41,
from /usr/include/c++/4.8.2/ios:42,
from /usr/include/c++/4.8.2/ostream:38,
from /usr/include/c++/4.8.2/iostream:39,
from 3.43.cpp:1:
/usr/include/c++/4.8.2/initializer_list:89:5: note: template<class _Tp> constexpr const _Tp* std::begin(std::initializer_list<_Tp>)
begin(initializer_list<_Tp> __ils) noexcept
I search it in google, but not find similar answer.
Since arr is just a pointer, there's no way to deduce how big it is. But, since you are actually passing in a real array, you can just template your function on its dimensions so you take the actual array by reference rather than having it decay to a pointer:
template <size_t X, size_t Y>
void foo(const int (&arr)[X][Y])
{
std::cout << "Dimensions are: " << X << "x" << Y << std::endl;
for (const int (&row)[Y] : arr) {
for (int val : row) {
std::cout << val << ' ';
}
std::cout << std::endl;
}
}
int main() {
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
foo(arr);
}
std::begin() and std::end() won't work for pointers. They only work for arrays. If you want to deduce the size of your array you'll need to pass it as a reference your function:
#include <cstddef>
template <std::size_t A, std::size_t B>
void version_1(int (&arr)[B][A]) {
for (const int (&p)[A] : arr) {
for (int q : p) {
cout << q << " ";
}
cout << '\n';
}
}
Pointers are not the same as arrays. To be able to use range based for, your container must support std::begin and std::end. Standard C arrays can be used, but not pointers.