In my C++20 project, I have a data container class Tensor
which handles multi-dimensional arrays. It is implemented by having a raw pointer to some slab of memory, and then it provides various form of access to it, and also methods for printing stuff, resetting it to zero, and so on.
The class doubles as its own view. This is done with the boolean field owns_allocation_
which tracks which Tensor
instance is responsible for deallocating the memory. This works fine, except for one detail: I cannot make a proper read-only view of a slice of a Tensor
object. The closest I get is the following:
#include <vector>
class Tensor {
public:
Tensor(std::vector<int> const &shape)
: shape_(shape), owns_allocation_(true) {
int size = 1;
for (int const dim : shape) {
size *= dim;
}
data_ = new double[size];
}
Tensor(double *data, std::vector<int> const &shape, bool owns_allocation) {
data_ = data;
shape_ = shape;
owns_allocation_ = owns_allocation;
}
Tensor(Tensor const &other) = delete;
// Move constructor and other methods …
~Tensor() {
if (owns_allocation_) {
delete[] data_;
}
}
double &at_flat(std::size_t const index) { return data_[index]; }
// Here is the problem, the return type of `Tensor const` is just `Tensor`.
Tensor const slice(std::vector<int> at, std::size_t const size) const {
auto const offset = compute_offset(shape_, at);
auto const new_shape = compute_slice_shape(shape_, at, size);
return Tensor(data_ + offset, new_shape, false);
}
// More handy methods …
private:
double *data_ = nullptr;
std::vector<int> shape_;
bool owns_allocation_;
};
void break_const(Tensor const &tensor) {
auto view = tensor.get_view();
view.at_flat(0) = 1.0;
}
int main() {
Tensor tensor({2, 2});
break_const(tensor);
}
But because a return type of Tensor const
doesn't have any influence on the caller, it will end up as a modifiable Tensor
instance and I get change the state of the object.
I could write a separate TensorView
class, which would re-implement all the methods which are already marked const
on Tensor
. But that seems like a violation of the “don't repeat yourself” principle.
The best idea I have so far is making a TensorView
class and derive a more general (mutable) Tensor
from that. Then all the const
methods would go into TensorView
and be automatically inherited. The data fields would be protected
. Would that make sense? Or is there something better?