The craay method is to not use elements directly.
Create a cumulative variation pattern. Create a data item template.
The data item template accepts a tag structure.
Replace data_member<tag,T>::operator^( tag ) to return a reference to T Mqybe does the same for the free operator^( data_member< tag, T >*, tag )
Now you can get the member through this^tag() , which looks like member access. If you make a global instance of tag , you can even remove () .
You also have a compilation time reflection for your data members. That way you can write for_each_member and write all your serialization code once and use it for each struct .
Access control and other data_member categories can be performed in the aggregate template.
Tag-based data construction can be accomplished using the sophisticated aggregate constructor.
Alternatively, you can wait for a real reflection in C ++ to appear, perhaps for a decade.
Alternatively, you can turn your struct into a tuple wrapper and use something like the above redefinition trick to get this^tag for name-based access.
If we have a struct foo containing int x, y and double d , we want to do this, we can do the following:
// boilerplate template<typename C, std::size_t idx> struct Tag {}; template<typename C, std::size_t tag_idx> auto operator^(C&& lhs, Tag<C, tag_idx> const&>) -> decltype( std::get<tag_idx>( std::forward<C>(lhs) ) { return std::get<tag_idx>( std::forward<C>(lhs); } struct foo:std::tuple< int, int, double > {}; Tag< foo, 0 > x; // another annoying part: need to manually number them Tag< foo, 1 > y; // we can avoid this via an aggregate trick, but Tag< foo, 2 > d; // even that isn't all that pretty int main() { foo bar; bar^x = 7; bar^y = 3; bar^d = 3.14; }
One (serious) problem is that two member variables in two different struct have the same “namespace” and conflict if they have the same name.