I have been devloping a class template for a stack-allocated N-dimensional array (note that I have not yet implemented support for >1-D arrays):
template <typename T, std::size_t... DIMS> class ndarray {
static constexpr std::size_t NDIM{sizeof...(DIMS)};
static constexpr std::array<T, NDIM> SHAPE{DIMS...};
static constexpr std::size_t SIZE{(1 * ... * DIMS)};
std::array<T, SIZE> _data;
public:
constexpr ndarray() : _data{} {}
constexpr ndarray(T element) : _data{element} {}
constexpr ndarray(const T (&elements)[SIZE])
: _data(std::to_array(elements)) {}
constexpr ndarray(const std::array<T, SIZE> &elements) : _data(elements) {}
constexpr std::size_t size() { return SIZE; }
constexpr std::array<T, NDIM> shape() { return SHAPE; }
constexpr ndarray &operator=(const T (&elements)[SIZE]) {
_data = std::to_array(elements);
}
};
template <typename T, std::size_t N>
ndarray(const T (&elements)[N]) -> ndarray<T, N>;
The above code allows for every method of initialization and assignment I am looking for except one:
// 0-D arrays
void zero() {
int z{1};
int x[1];
ndarray a(1);
ndarray b{1};
ndarray c = 1;
ndarray<int> d;
d = 1;
ndarray e(z);
ndarray f{z};
ndarray g = z;
ndarray<int> h;
h = z;
}
// 1-D arrays
void one() {
int y[]{1, 2};
std::array z{1, 2};
ndarray a({1, 2});
ndarray b{{1, 2}};
ndarray c = {1, 2};
ndarray<int, 2> d;
d = {1, 2};
ndarray e(y);
ndarray f{y};
ndarray g = y;
ndarray<int, 2> h;
h = y;
ndarray i(z);
ndarray j{z};
ndarray k = z;
ndarray<int, 2> l;
l = z;
}
error C2641: cannot deduce template arguments for 'ndarray'
error C2780: 'ndarray<T,N> ndarray(const T (&)[N])': expects 1 arguments - 2 provided
note: see declaration of 'ndarray'
error C2780: 'ndarray<T,DIMS...> ndarray(const std::array<T,ndarray<T,DIMS...>::SIZE> &)': expects 1 arguments - 2 provided
note: see declaration of 'ndarray'
error C2780: 'ndarray<T,DIMS...> ndarray(const T (&)[ndarray<T,DIMS...>::SIZE])': expects 1 arguments - 2 provided
note: see declaration of 'ndarray'
error C2780: 'ndarray<T,DIMS...> ndarray(T)': expects 1 arguments - 2 provided
note: see declaration of 'ndarray'
error C2780: 'ndarray<T,DIMS...> ndarray(void)': expects 0 arguments - 2 provided
note: see declaration of 'ndarray'
error C2780: 'ndarray<T,DIMS...> ndarray(ndarray<T,DIMS...>)': expects 1 arguments - 2 provided
note: see declaration of 'ndarray'
The copy-list-initialization of c
in one
is the cause of the compiler error. ndarray c = {{1, 2}}
works, but this worries me because it is easily confused with an array of shape [1, N]. It also means that ndarray foo = 1
is equivalent to ndarray foo = {1}
, which I strongly dislike.
Unfortunately, I cannot use either std::initializer_list
or parameter pack constructor, as they would lead to some ambiguous cases (would ndarray foo{1}
have shape [] or [1]?). The only option I have thought of is to make the constructors explicit to prevent copy-list-initialization entirely, although doing this would limit my class's interoperatibility with its contained type to some extent. Is there another way around this?
Edit: I just realized that ndarray foo({1})
is also ambiguous as it stands. Very frustrating.