Double-Bound Array 2: Copyable

This version of the double-bounded array correctly implements copying of objects.

#ifndef _BOUNDARR_H_ #define _BOUNDARR_H_ /* * This class implements an array with user-specified bounds. Subscripting * is done with a at() function, and subscripts are bounds-checked. */ #include <iostream> #include <string> #include <stdexcept> #include <memory> #include <algorithm> template <typename T> class BoundArr { public: // Create a new array of the given dimensions BoundArr(int low, int high): m_space(new T[high-low+1]), m_low(low), m_high(high) { } // Note: I think m_space(std::make_shared<T[]>(high-low+1)) should // work also, but it blows up with a farily indecipherable memory // corruption error. Seems to be a 2020 standard behavior, so // compiler may not have it right yet, or maybe 2020 just breaks // everything it touches. // Convenience for conventional creation. BoundArr(int size): m_space(new T[size]), m_low(0), m_high(size-1) { } // Create an array as a copy of another. BoundArr(const BoundArr &other) { copyin(other); } // Perform an array assignment. Delete our space, then make us a // copy of other. BoundArr & operator=(const BoundArr &other) { copyin(other); return *this; } // Find out how large this is. int low() const { return m_low; } int high() const { return m_high; } int size() const { return m_high - m_low + 1; } // Subscript the array. The two versions are allowed, and will // be chosen based on the context of use. T &at(int subs) { return m_space[offset(subs)]; } T const &at(int subs) const { return m_space[offset(subs)]; } // Print the array to the stream. void print(std::ostream &out) const; private: std::shared_ptr<T[]> m_space; // Actual storage space for the array. int m_low, m_high; // Bounds // Make us a copy of the other array. Allocates m_space of // the same size, then copies the contents of other.m_size into it, // and copies m_size. void copyin(const BoundArr<T> &other); // Check the bounds, throw if out, then int offset(int sub) const; }; // Make us a copy of the other array. Allocates m_space of the same size, then // copies the contents of other.m_size into it, and copies m_size. template <typename T> void BoundArr<T>::copyin(const BoundArr<T> &other) { // The proper way to set m_space is the following, which is C++ 17 // standard. It won't compile, even with the relevant compiler option. // I believe this is some error in the compiler installation on // Fedora, but I don't really know. The reset and new gets the // job done. // m_space = std::make_shared<T[]>(other.size()); // Replace any existing space with a new block // having the same size and content as other. m_space.reset(new T[other.size()]); m_low = other.m_low; m_high = other.m_high; for(int i = 0; i < size(); ++i) m_space[i] = other.m_space[i]; } // Check that the sub is in range, and throw if it is not. Otherwise, // return the sub adjusted to an offset for subscripting the base array. template <typename T> int BoundArr<T>::offset(int sub) const { if(sub < m_low || m_high < sub) throw std::range_error("Bounds: sub " + std::to_string(sub) + " outside " + std::to_string(m_low) + ".." + std::to_string(m_high)); return sub - m_low; } // Array printer template <typename T> void BoundArr<T>::print(std::ostream &out) const { out << "<" << m_low << "|"; for(int n = 0; n < size(); ++n) { out << " " << m_space[n]; } out << " |" << m_high << ">"; } // This exports the printing function to standard streams by defining // the meaning of << on a stream and a BoundArr to be running the print // method on the array to the stream. template <typename T> inline std::ostream & operator<<(std::ostream &strm, const BoundArr<T> &arr) { arr.print(strm); return strm; } #endif