Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to loop over different data members of an object in C++

Tags:

c++

If I have a class object comprised of 3 doubles; x, y and z, is there a way to loop over them in a function?

e.g.

for(i in (x,y,z))
    do something;

I can do something similar with explicit casting, but I am wondering if there is a more elegant solution to this.

class testc {
public:
    double x, y, z;

    testc(double x, double y, double z)
        :x(x), y(y), z(z)
    {}
};

int main()
{
    testc omega(1, 2, 3);
    cout << *(double*)&omega << " " << *((double*)&omega +1) << " " << *((double*)&omega +2);
}
like image 450
Mare Sorin-Alexandru Avatar asked Jun 11 '20 15:06

Mare Sorin-Alexandru


People also ask

Can you loop through a struct?

In programming, iteration (commonly known as looping) is a process where a step is repeated n number of times until a specific condition is met. Just like every other programming language, Golang has a way of iterating through different data structures and data types like structs, maps, arrays, strings, and so on.

Can you use a for each loop with vector?

Working of the foreach loop in C++ So basically a for-each loop iterates over the elements of arrays, vectors, or any other data sets. It assigns the value of the current element to the variable iterator declared inside the loop.

What are range based for loops?

Range-based for loop (since C++11) Executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.


1 Answers

(C++20) Looping over a known number of public members: structured bindings in a range-based for loop initialization statement

As of C++20 we may combine structured bindings with range-based for loop initialization statements (the latter is a C++20 feature):

Grammar

for ( init-statement(optional) range-declaration : range-expression ) ...

Specifically, using structured bindings as the init-statement in a range-based for loop:

#include <iostream>

class Foo {
public:
    double x, y, z;

    Foo(double x, double y, double z) : x(x), y(y), z(z) {}
};

int main() {
    const Foo foo(1., 2., 3.);

    for (auto [x, y, z] = foo; auto e : {x, y, z}) {
        std::cout << e << " ";
    } // 1 2 3

    return 0;
}

Note, however, that you can only use structured bindings to decompose public members of your class (in your example all members are public). Moreover, for the initializer list in the range-expression of the range based for loop, you may not have conflicting types, meaning this approach is limited to the context of your example: where all public members are of the same type.

Why use the structured bindings instead of just list-initializing the class members directly?

Now, if the (public) members of your class as well as the instance of it all have very brief names, we may want to consider omitting the structured bindings and instead list-initialize the class members directly:

const Foo f(1., 2., 3.);
for (auto e : {f.x, f.y, f.z}) {
    std::cout << e << " ";
} // 1 2 3

However, albeit arguably briefer, the drawback is that we no longer get any help from the compiler in spotting whether we've actually exactly decomposed all public members (no less, no more) or not, a check that is present when we use structured bindings:

for (auto [x, y] = foo; auto e : {x, y}) { /* ... */ }
// error: type 'Foo' decomposes into 3 elements, 
//        but only 2 names were provided

for (auto [x, y, z, xx] = foo; auto e : {x, y, z, xx}) { /* ... */ }
// error: type 'Foo' decomposes into 3 elements, 
//        but 4 names were provided
like image 172
dfrib Avatar answered Oct 21 '22 07:10

dfrib