If macros are allowed then it seems doable.
First attempt (nice but not perfect...)
int main() {
Vector<int, 4> vec;
vec[0] = 1; // same as: vec.t1 = 1;
vec[1] = 2; // same as: vec.t2 = 2;
vec[2] = 3; // same as: vec.t3 = 3;
vec[3] = 4; // same as: vec.t4 = 4;
std::cout << vec.t1 + vec.t2 + vec.t3 + vec.t4; // 10
}
To achieve the above:
#define VAR_NAME(num) t##num
#define DefineVector(num) \
template<typename T> \
struct Vector<T, num> : Vector<T, num-1> { \
T VAR_NAME(num); \
T& operator[](int index) { \
if(index == num-1) return VAR_NAME(num); \
return Vector<T, num-1>::operator[](index); \
} \
}
template<typename T, size_t N>
struct Vector;
template<typename T>
struct Vector<T, 1> {
T t1;
T& operator[](int index) {
// in case index != 0 this is UB
return t1;
}
};
DefineVector(2);
DefineVector(3);
DefineVector(4);
// TODO:
// replace 3 declarations above with a single *DefineVectorsRecursively(4);*
// by using recursive macros
// see: https://stackoverflow.com/questions/12447557/can-we-have-recursive-macros
// leaving this as a further exercise...
http://coliru.stacked-crooked.com/a/42625e9c198e1e58
EDIT: Added operator[] to address the concern raised in comment.
Second attempt: with nicer field names
The OP requested the fields to have nicer names, like x, y, z.
This is a challenge. But macros come again to the rescue:
int main() {
Vector<int, 3> vec;
vec[0] = 1;
vec[1] = 2;
vec[2] = 3;
std::cout << vec.x + vec.y + vec.z; // 6
}
With the following code:
#include <boost/preprocessor/variadic/size.hpp>
template<typename T, size_t DIMENSIONS>
struct Vector;
#define DefineVector(VAR, ...) \
template<typename T> \
struct Vector<T, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__) + 1> \
: Vector<T, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)> { \
T VAR; \
T& operator[](int index) { \
if(index == BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)) return VAR; \
return Vector<T, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)>::operator[](index); \
} \
}
#define DefineVector1(VAR) \
template<typename T> \
struct Vector<T, 1> { \
T VAR; \
T& operator[](int index) { \
/* in case index != 0 this is UB */ \
return VAR; \
} \
}
DefineVector1(x);
DefineVector(y, x);
DefineVector(z, y, x);
// TODO: create recursive macro for DefineVector(z, y, x)
// that will create the two above recursively
Code: http://coliru.stacked-crooked.com/a/2550eede71dc9b5e
But wait, operator[] is not so efficient
I had a thought of having a more efficient operator[] in case T is a standard layout type, with the following implementation:
#define DefineVector(VAR, ...) \
template<typename T> \
struct Vector<T, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__) + 1> \
: Vector<T, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)> { \
T VAR; \
T& operator[](int index) { \
if constexpr(std::is_standard_layout_v<T>) { \
return *(&VAR - (BOOST_PP_VARIADIC_SIZE(__VA_ARGS__) - index)); \
} else { \
if(index == BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)) return VAR; \
return Vector<T, BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)>::operator[](index); \
} \
} \
}
The (BAD) attempt: http://coliru.stacked-crooked.com/a/d367e770f107995f
Unfortunately - above optimization is illegal
For the presented implementation, any Vector with DIMENSIONS > 1 is not a standard layout class (even if T is) because it has members both in base class and in the derived class:
10.1 [class.prop]
[3] A class S is a standard-layout class if it: ...
[3.6] has all non-static data members and bit-fields in the class and its base
classes first declared in the same class ...
So the optimization attempt above has undefined behavior - compiler is not obliged to keep the address of the members in the inheritance hierarchy in their order.
The initial solution is still valid.