I have a class that includes several members of type double.
Suppose I need to make a function that re-orders a vector of class objects based on the values of one of the members on the class. So:
class myClass{
...
public:
double x, y, z;
...
}
void SpecialSort_x(std::vector<myClass>& vec) {
// re-order stuff according to values of vec[i].x
...
}
But now, I want to be able to do the same re-ordering, but according to values of the other members of the class (y and z in the code above).
Instead of making two more functions that are identical to the first one, except with all references to x changed to y or z, I would like to make a single polymorphic function that can re-order the vector according to any of the members of myClass.
What is the best way to do this?
You can use std::sort, combined with a lambda and a pointer to member thus:
#include <vector>
#include <algorithm>
class MyClass
{
public:
double x, y, z;
};
typedef double MyClass::* Field;
void specialSort(std::vector<MyClass>& vec, Field field)
{
std::sort(vec.begin(), vec.end(), [field](const MyClass & a, const MyClass & b) -> bool
{
return a.*field < b.*field;
});
}
int main()
{
std::vector<MyClass> vec;
Field member = &MyClass::x;
specialSort(vec, member);
return 0;
}
And you could also templatise the sort using:
template<class T>
void specialSort(std::vector<T>& vec, double T::* field)
{
std::sort(vec.begin(), vec.end(), [field](const T& a, const T& b) -> bool
{
return a.*field < b.*field;
});
}
I agree with everyone suggesting alternate approaches given the problem description here.
However, if you ever really have the need to access a class member chosen at runtime, you can use a pointer-to-member type. There is usually a more elegant way to accomplish the effect you want, though.
For example:
#include <iostream>
#include <vector>
struct X {
double a;
double b;
double c;
};
void operate_on_member(const X& x, double X::*pm)
{
std::cout << x.*pm << '\n';
}
int main()
{
std::vector<X> xs {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};
for (const auto& x : xs)
operate_on_member(x, &X::a);
for (const auto& x : xs)
operate_on_member(x, &X::b);
for (const auto& x : xs)
operate_on_member(x, &X::c);
}
Related
I have a function that takes a vector-like input. To simplify things, let's use this print_in_order function:
#include <iostream>
#include <vector>
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<int> printme = {100, 200, 300};
std::vector<int> order = {2,0,1};
print_in_order(order, printme);
}
Now I have a vector<Elem> and want to print a single integer member, Elem.a, for each Elem in the vector. I could do this by creating a new vector<int> (copying a for all Elems) and pass this to the print function - however, I feel like there must be a way to pass a "virtual" vector that, when operator[] is used on it, returns this only the member a. Note that I don't want to change the print_in_order function to access the member, it should remain general.
Is this possible, maybe with a lambda expression?
Full code below.
#include <iostream>
#include <vector>
struct Elem {
int a,b;
Elem(int a, int b) : a(a),b(b) {}
};
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<Elem> printme = {Elem(1,100), Elem(2,200), Elem(3,300)};
std::vector<int> order = {2,0,1};
// how to do this?
virtual_vector X(printme) // behaves like a std::vector<Elem.a>
print_in_order(order, X);
}
It's not really possible to directly do what you want. Instead you might want to take a hint from the standard algorithm library, for example std::for_each where you take an extra argument that is a function-like object that you call for each element. Then you could easily pass a lambda function that prints only the wanted element.
Perhaps something like
template<typename vectorlike, typename functionlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme,
functionlike func) {
for (int i : order)
func(printme[i]);
}
Then call it like
print_in_order(order, printme, [](Elem const& elem) {
std::cout << elem.a;
});
Since C++ have function overloading you can still keep the old print_in_order function for plain vectors.
Using member pointers you can implement a proxy type that will allow you view a container of objects by substituting each object by one of it's members (see pointer to data member) or by one of it's getters (see pointer to member function). The first solution addresses only data members, the second accounts for both.
The container will necessarily need to know which container to use and which member to map, which will be provided at construction. The type of a pointer to member depends on the type of that member so it will have to be considered as an additional template argument.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
private:
const Container * m_container;
MemberPtr m_member;
};
Next, implement the operator[] operator, since you mentioned that it's how you wanted to access your elements. The syntax for dereferencing a member pointer can be surprising at first.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// Dispatch to the right get method
auto operator[](const size_t p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To use this implementation, you would write something like this :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
virtual_vector<decltype(printme), decltype(&Elem::a)> X(printme, &Elem::a);
print_in_order(order, X);
}
This is a bit cumbersome since there is no template argument deduction happening. So lets add a free function to deduce the template arguments.
template<class Container, class MemberPtr>
virtual_vector<Container, MemberPtr>
make_virtual_vector(const Container & p_container, MemberPtr p_member_ptr)
{
return{ p_container, p_member_ptr };
}
The usage becomes :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
If you want to support member functions, it's a little bit more complicated. First, the syntax to dereference a data member pointer is slightly different from calling a function member pointer. You have to implement two versions of the operator[] and enable the correct one based on the member pointer type. Luckily the standard provides std::enable_if and std::is_member_function_pointer (both in the <type_trait> header) which allow us to do just that. The member function pointer requires you to specify the arguments to pass to the function (non in this case) and an extra set of parentheses around the expression that would evaluate to the function to call (everything before the list of arguments).
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// For mapping to a method
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == true, const size_t> p_index) const
{
return ((*m_container)[p_index].*m_member)();
}
// For mapping to a member
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == false, const size_t> p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To test this, I've added a getter to the Elem class, for illustrative purposes.
struct Elem {
int a, b;
int foo() const { return a; }
Elem(int a, int b) : a(a), b(b) {}
};
And here is how it would be used :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
{ // print member
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
{ // print method
auto X = make_virtual_vector(printme, &Elem::foo);
print_in_order(order, X);
}
}
You've got a choice of two data structures
struct Employee
{
std::string name;
double salary;
long payrollid;
};
std::vector<Employee> employees;
Or alternatively
struct Employees
{
std::vector<std::string> names;
std::vector<double> salaries;
std::vector<long> payrollids;
};
C++ is designed with the first option as the default. Other languages such as Javascript tend to encourage the second option.
If you want to find mean salary, option 2 is more convenient. If you want to sort the employees by salary, option 1 is easier to work with.
However you can use lamdas to partially interconvert between the two. The lambda is a trivial little function which takes an Employee and returns a salary for him - so effectively providing a flat vector of doubles we can take the mean of - or takes an index and an Employees and returns an employee, doing a little bit of trivial data reformatting.
template<class F>
struct index_fake_t{
F f;
decltype(auto) operator[](std::size_t i)const{
return f(i);
}
};
template<class F>
index_fake_t<F> index_fake( F f ){
return{std::move(f)};
}
template<class F>
auto reindexer(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return v[f(i)];
});
};
}
template<class F>
auto indexer_mapper(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return f(v[i]);
});
};
}
Now, print in order can be rewritten as:
template <typename vectorlike>
void print(vectorlike const & printme) {
for (auto&& x:printme)
std::cout << x << std::endl;
}
template <typename vectorlike>
void print_in_order(std::vector<int> const& reorder, vectorlike const & printme) {
print(reindexer([&](auto i){return reorder[i];})(printme));
}
and printing .a as:
print_in_order( reorder, indexer_mapper([](auto&&x){return x.a;})(printme) );
there may be some typos.
I'm not an advanced programmer. How can I overload the [] operator for a class that has two (or more) array/vector type variables?
class X
{
protected:
std::vector<double> m_x, m_y;
public:
double& operator[](const short &i) { return ???; }
};
What should I use for ???, or how can I do it (maybe adding other definitions?) to be able to call either variable?
Additional question: will this allow other classes of type class derived : public X access m_x and m_y for writing?
UPDATE:
Thank you everyone who answered, but I'm afraid that if I draw the line then the answer to my first question is no, and to the second yes. The longer version implies either an extra struct, or class, or plain setters/getters, which I wanted to avoid by using a simple function for all.
As it stands, the current solution is a (temporary) reference to each variable, in each class to avoid the extra X:: typing (and keep code clear), since m_x would have existed, one way or another.
you can write just a function for this, like:
double &get(unsigned int whichVector, unsigned int index)
{
return (whichVector == 0 ? m_x[index] : m_y[index]);
}
or use operator():
struct A
{
std::vector<int> a1;
std::vector<int> a2;
int operator()(int vec, int index)
{
return (vec == 0 ? a1[index] : a2[index]);
}
};
A a;
auto var = a(0, 1);
but still, this is kinda strange :) probably you should just give a const ref outside, like:
const std::vector<double> &getX() const { return m_x; }
and second question: protected will be convert into private in public inheritance (child/derived will have access to these memebers)
Assuming you want m_x and m_y indexed against the same parameter and a single return value:
struct XGetter
{
double& x;
double& y;
};
XGetter operator[](const short &i) { return { m_x[i], m_y[i] }; }
And the const overload:
struct XGetterReadOnly
{
double x;
double y;
};
XGetterReadOnly operator[](const short &i) const { return { m_x[i], m_y[i] }; }
The compiler will make a good job of optimizing away the intermediate classes XGetter and XGetterReadOnly where appropriate which maybe hard to get your head round if you're a new to C++.
If using mixin doesn't make you uncomfortable you could use tag dispatching like:
#include <utility>
#include <vector>
#include <iostream>
template <size_t I>
struct IndexedVector {
std::vector<double> v;
IndexedVector():v(10){}
};
template <size_t I>
struct tag {
int i;
};
template <size_t S, class = std::make_index_sequence<S>>
struct MixinVector;
template <size_t S, size_t... Is>
struct MixinVector<S, std::index_sequence<Is...>>: IndexedVector<Is>... {
template <size_t I>
double &operator[](tag<I> i) {
return IndexedVector<I>::v[i.i];
}
};
int main() {
MixinVector<2> mv;
mv[tag<0>{0}] = 1.0;
std::cout << mv[tag<0>{0}] << std::endl;
}
To use std::index_sequence you need however compiler supporting c++14 (you could though implement it yourself in c++11). The approach is easily expandable to any number of vectors by simple MixinVector template parameter modification.
There are many broken things, either at conceptual and design level.
Are you able to point your finger simultaneously against two distinct things? No? That's why you cannot use one index to address two distinct vector retaining their distinction.
You can do many things: whatever way to "combine" two value int one is good
by a syntactic point of view:
return m_x[i]+m_y[x] or return sin(m_x[i])*cos(m_y[i]) or return whatever_complicated_expression_you_like_much
But what's the meaning of that? The point is WHY THERE ARE TWO VECTOR IN YOUR CLASS? What do you want them to represent? What do you mean (semantically) indexing them both?
Something I can do to keep their distinction is
auto operator[](int i) const
{ return std::make_pair(m_x[i],m_y[i]); }
so that you get a std::pair<double,double> whose fist and second members are m_x[i] and m_y[i] respectively.
Or ... you can return std::vector<double>{m_x[i],m_y[i]};
About your other question: Yes, inheriting as public makes the new class able to access the protected parts: that's what protected is for.
And yes, you cam R/W: public,protected and private are about visibility, not readability and writeability. That's what const is about.
But again: what does your class represent? without such information we cannot establish what make sense and what not.
Ok, stated your comment:
you need two different funcntions: one for read (double operator[](unsigned) const) and one for write (double& operator[](unsigned) const)
If you know vectors have a known length -say 200-, that you can code an idex transforamtion like i/1000 to identify the vector and i%1000 to get the index,so that 0..199 addres the first, 1000..1199 address the second 2000..2199 address the third... etc.
Or ... you can use an std::pair<unsigned,unsigend> as the index (like operator[](const std::pair<unsigned,unsigned>& i), using i.first to identify the vector, and i.second to index into it, and then call x[{1,10}], x[{3,30}] etc.
Or ... you can chain vetor together as
if(i<m_x.size()) return m_x[i]; i-=m_x:size();
if(i<m_y.size()) return m_y[i]; i-=m_y:size();
if(i<m_z.size()) return m_z[i]; i-=m_z:size();
...
so that you index them contiguously.
But you can get more algorithmic solution using an array of vectors instead of distinct vector variables
if you have std::array<std::vector<double>,N> m; instead of m_x, m_y and m_z the above code can be...
for(auto& v: m)
{
if(i<v.size()) return v[i];
i-=v.size();
}
You can return a struct has two double
struct A{
double& x;
double& y;
A(A& r) : x(r.x), y(r.y){}
A(double& x, double& y) : x(x), y(y){}
};
class X
{
protected:
std::vector<double> m_x, m_y;
public:
A operator[](const short &i) {
A result(m_x[i], m_y[i]);
return result;
}
};
Thank for editing to #marcinj
This question already has answers here:
Iterating over a struct in C++
(8 answers)
Closed 1 year ago.
Is it possible in C++ to iterate through a Struct or Class to find all of its members? For example, if I have struct a, and class b:
struct a
{
int a;
int b;
int c;
}
class b
{
public:
int a;
int b;
private:
int c;
}
Would it be possible to loop them to say get a print statement saying "Struct a has int named a, b, c" or "Class b has int named a, b, c"
There are a couple of ways to do this, but you need to use some macros to either define or adapt the struct.
You can use the REFLECTABLE macro given in this answer to define the struct like this:
struct A
{
REFLECTABLE
(
(int) a,
(int) b,
(int) c
)
};
And then you can iterate over the fields and print each value like this:
struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};
template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}
A x;
print_fields(x);
Another way is to adapt the struct as a fusion sequence (see the documentation). Here's an example:
struct A
{
int a;
int b;
int c;
};
BOOST_FUSION_ADAPT_STRUCT
(
A,
(int, a)
(int, b)
(int, c)
)
Then you can print the fields as well using this:
struct print_visitor
{
template<class Index, class C>
void operator()(Index, C & c)
{
std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call()
<< "="
<< boost:::fusion::at<Index>(c)
<< std::endl;
}
};
template<class C>
void print_fields(C & c)
{
typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}
No, it's not possible, because there is no reflection in C++.
If you have members of the same type (as you do in your first specific example) that you want to both (a) have names, and (b) be iterable, then you can combine an array with an enum:
enum names { alice, bob, carl };
struct myStruct;
{
std::array<int, 3> members;
}
Then you can both
myStruct instance;
// iterate through them...
for (auto elem : instance.members)
{
// work with each element in sequence
}
// and call them by name, taking away the need to remember which element is the first, etc.
instance.members[bob] = 100;
Clearly not a general solution, but I've found this useful in my own work.
Provided your member variables are of the same type, you can do something like this that i stole from the GLM library:
class Point
{
Point();// you must re-implement the default constructor if you need one
union
{
struct
{
double x;
double y;
double z;
};
std::array<double, 3> components;
};
};
Admittedly this isn't the most elegant solution from a maintainability standpoint, manually keeping count of the number of variables you have is asking for trouble. However It will work without additional libraries or macros and is applicable in most situations that you'd want this behaviour.
Unions don't support automatically generated default constructors so you'll need to write one that tells the object how to initialise the union.
for (double component : point.components)
{
// do something
}
Assuming that all class members are of the same type, you may employ a C++17 feature called structured binding. Assuming that all members are public this would work:
struct SI
{
int x;
int y;
int z;
};
struct SD
{
double x;
double y;
double z;
};
template<typename T>
void print(const T &val)
{
const auto& [a, b, c] = val;
for (auto elem : {a, b, c})
{
std::cout << elem << " ";
}
std::cout << std::endl;
}
This would work with any struct that has exactly 3 public elements of the same (copyable) type. In case of non-public members the function must be a friend or a member. This approach however cannot be easily extented to arbitrary number of elements.
This is an improved version of QCTDevs answer:
class MyClass
{
union
{
struct Memberstruct
{
double test0;
double test1;
double test2;
} m;
array<double, sizeof( Memberstruct ) / sizeof( double )> memberarray;
};
bool test() { &(m.test1) == &(memberarray[1]); }
};
The requirement is still to have all the same datatypes, and you also need to implement the default Constructor, if needed.
It is improved in that you don't need to manually maintain the size of the array.
A drawback is an altered syntax in comparison to the class without this workaround.
I want to sort out a vector using std::sort with self-defined comparison function/functor. Inside this function I always want to have access functions or variables defined within the class.
Class MainClass
{
protected: // Variables
float fixedpoint;
protected: // Methods
float DistanceBetweenTwoPoints(float,float);
void function1();
struct CompareDistanceToGoal : public std::binary_function<float,float,bool>
{
bool operator()(float p1, float p2)
{
// return ( p1 < p2);
// I want to use the following code instead of the above
return DistanceBetweenTwoPoints(fixedpoint,p1) < DistanceBetweenTwoPoints(fixedpoint,p2);
}
};
}
Inside function1:
void MainClass::function1()
{
std::vector<float> V1;
std::sort(V1.begin(),V1.end(),MainClass::CompareDistanceToGoal());
}
So instead of using "return (p1 < p2)", I want to have access to fixedpoint and maybe DistanceBetweenTwoPoints() function. Is this possible (i.e. using friend identifier somehow)?
Can anybody show me how to do this? Thanks.
As a nested type, CompareDistanceToGoal has access to all members of MainClass; there's no need to declare it a friend. (Although this is a moderately recent change to the language; a compiler that doesn't implement C++11 might need a friend declaration, friend CompareDistanceToGoal;, to match modern behaviour).
However, since these members are non-static, you can't do anything with those members unless you provide a MainClass object. Perhaps you want to make them static; or perhaps you want to "capture" an object:
struct CompareDistanceToGoal // no need for that binary_function nonsense
{
MainClass & mc;
CompareDistanceToGoal(MainClass & mc) : mc(mc) {}
bool operator()(float p1, float p2)
{
return mc.DistanceBetweenTwoPoints(mc.fixedpoint,p1) <
mc.DistanceBetweenTwoPoints(mc.fixedpoint,p2);
}
};
std::sort(V1.begin(),V1.end(),MainClass::CompareDistanceToGoal(some_main_class_object));
It's a bit hard to know what you're trying to do, but this seems to have as much chance of being what you want as anything else... note that MainClass stores a fixedpoint then provides a functor (no need for a nested class) which is then used by sort. In the code below, it sorts the vector so elements closest to the MainClass fixedpoint are earlier in the vector. See it running at ideone.com.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
class MainClass
{
public:
MainClass(float fixedpoint) : fixedpoint_(fixedpoint) { }
bool operator()(float p1, float p2) const
{
float d1 = DistanceBetweenTwoPoints(fixedpoint_,p1);
float d2 = DistanceBetweenTwoPoints(fixedpoint_,p2);
return d1 < d2 || d1 == d2 && p1 < p2;
};
protected: // Variables
float fixedpoint_;
static float DistanceBetweenTwoPoints(float a,float b) { return std::fabs(a - b); }
void function1();
};
int main()
{
std::vector<float> v { 1, 3, 4.5, 2.3, 9, 12 };
std::sort(std::begin(v), std::end(v), MainClass(9.3));
for (auto f : v)
std::cout << f << '\n';
}
you can capture value manually
struct CompareDistanceToGoal : public std::binary_function<float,float,bool>
{
float fixedpoint;
CompareDistanceToGoal(float p) : fixedpoint(p) {}
bool operator()(float p1, float p2)
{
return DistanceBetweenTwoPoints(fixedpoint,p1) < DistanceBetweenTwoPoints(fixedpoint,p2);
}
};
and use it
void MainClass::function1()
{
std::vector<float> V1;
std::sort(V1.begin(),V1.end(),MainClass::CompareDistanceToGoal(fixedpoint));
}
or if C++11 is available, use lambda to capture value
void MainClass::function1()
{
std::vector<float> V1;
std::sort(V1.begin(),V1.end(),[=](float p1, float p2){
return DistanceBetweenTwoPoints(fixedpoint,p1) < DistanceBetweenTwoPoints(fixedpoint,p2);
});
}
This question already has answers here:
Iterating over a struct in C++
(8 answers)
Closed 1 year ago.
Is it possible in C++ to iterate through a Struct or Class to find all of its members? For example, if I have struct a, and class b:
struct a
{
int a;
int b;
int c;
}
class b
{
public:
int a;
int b;
private:
int c;
}
Would it be possible to loop them to say get a print statement saying "Struct a has int named a, b, c" or "Class b has int named a, b, c"
There are a couple of ways to do this, but you need to use some macros to either define or adapt the struct.
You can use the REFLECTABLE macro given in this answer to define the struct like this:
struct A
{
REFLECTABLE
(
(int) a,
(int) b,
(int) c
)
};
And then you can iterate over the fields and print each value like this:
struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};
template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}
A x;
print_fields(x);
Another way is to adapt the struct as a fusion sequence (see the documentation). Here's an example:
struct A
{
int a;
int b;
int c;
};
BOOST_FUSION_ADAPT_STRUCT
(
A,
(int, a)
(int, b)
(int, c)
)
Then you can print the fields as well using this:
struct print_visitor
{
template<class Index, class C>
void operator()(Index, C & c)
{
std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call()
<< "="
<< boost:::fusion::at<Index>(c)
<< std::endl;
}
};
template<class C>
void print_fields(C & c)
{
typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), boost::ref(c), _1));
}
No, it's not possible, because there is no reflection in C++.
If you have members of the same type (as you do in your first specific example) that you want to both (a) have names, and (b) be iterable, then you can combine an array with an enum:
enum names { alice, bob, carl };
struct myStruct;
{
std::array<int, 3> members;
}
Then you can both
myStruct instance;
// iterate through them...
for (auto elem : instance.members)
{
// work with each element in sequence
}
// and call them by name, taking away the need to remember which element is the first, etc.
instance.members[bob] = 100;
Clearly not a general solution, but I've found this useful in my own work.
Provided your member variables are of the same type, you can do something like this that i stole from the GLM library:
class Point
{
Point();// you must re-implement the default constructor if you need one
union
{
struct
{
double x;
double y;
double z;
};
std::array<double, 3> components;
};
};
Admittedly this isn't the most elegant solution from a maintainability standpoint, manually keeping count of the number of variables you have is asking for trouble. However It will work without additional libraries or macros and is applicable in most situations that you'd want this behaviour.
Unions don't support automatically generated default constructors so you'll need to write one that tells the object how to initialise the union.
for (double component : point.components)
{
// do something
}
Assuming that all class members are of the same type, you may employ a C++17 feature called structured binding. Assuming that all members are public this would work:
struct SI
{
int x;
int y;
int z;
};
struct SD
{
double x;
double y;
double z;
};
template<typename T>
void print(const T &val)
{
const auto& [a, b, c] = val;
for (auto elem : {a, b, c})
{
std::cout << elem << " ";
}
std::cout << std::endl;
}
This would work with any struct that has exactly 3 public elements of the same (copyable) type. In case of non-public members the function must be a friend or a member. This approach however cannot be easily extented to arbitrary number of elements.
This is an improved version of QCTDevs answer:
class MyClass
{
union
{
struct Memberstruct
{
double test0;
double test1;
double test2;
} m;
array<double, sizeof( Memberstruct ) / sizeof( double )> memberarray;
};
bool test() { &(m.test1) == &(memberarray[1]); }
};
The requirement is still to have all the same datatypes, and you also need to implement the default Constructor, if needed.
It is improved in that you don't need to manually maintain the size of the array.
A drawback is an altered syntax in comparison to the class without this workaround.