I am having a problem understanding what is wrong with the way I set up my pointers.
I am trying to do something along the following:
matrix[0] = new Array<T>(columnSize);
But it doesn't compile. I think the problem is that matrix[0] returns a reference, and assigning new Array(columnSize) to it isn't legal?
Here's the error
/Users/Ezequiel/desktop/wow/Matrix.h: In constructor ‘Matrix<T>::Matrix(size_t, size_t) [with T = int]’:
main.cpp:8: instantiated from here
/Users/Ezequiel/desktop/wow/Matrix.h:18: error: invalid conversion from ‘Array<int>*’ to ‘long unsigned int’
/Users/Ezequiel/desktop/wow/Matrix.h:18: error: initializing argument 1 of ‘Array<T>::Array(size_t) [with T = Array<int>*]’
I attached all of my code below.
// Array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <string>
#include <iostream>
#include <algorithm>
template<class T>
class Array {
private:
T* elements;
size_t low, high;
void init(const size_t, const size_t);
void copy(const Array<T>&);
public:
size_t size;
Array();
~Array();
Array(const size_t);
Array(const size_t, const size_t);
Array(const Array<T>&);
T& operator[](const size_t);
Array<T>& operator=(const Array<T>&);
template<class X>
friend std::ostream& operator<<(std::ostream&, const Array<X>&);
};
template<class T>
void Array<T>::init(const size_t low, const size_t high) {
if (high < low || (high - low + 1 <= 0)) {
std::cout << "An error was found in the bounds passed to the constructor.\n";
exit(1);
}
size = high - low + 1;
elements = new T[size];
this->low = low;
this->high = high;
}
template<class T>
void Array<T>::copy(const Array<T>& someArray) {
delete[] elements;
if (someArray.size) {
elements = new T[someArray.size];
std::copy(someArray.elements, someArray.elements + someArray.size, elements);
low = someArray.low;
high = someArray.high;
size = someArray.size;
} else {
size = 0;
elements = 0;
low = high = -1;
}
}
template<class T>
Array<T>::Array() {
size = 0;
elements = 0;
low = high = -1;
}
template<class T>
Array<T>::Array(const size_t size) {
init(0, size - 1);
}
template<class T>
Array<T>::Array(const size_t low, const size_t high) {
init(low, high);
}
template<class T>
Array<T>::~Array() {
delete[] elements;
}
template<class T>
Array<T>::Array(const Array<T>& someArray) {
copy(someArray);
}
template<class T>
Array<T>& Array<T>::operator=(const Array& someArray) {
if (this == &someArray) {
return *this;
}
copy(someArray);
return *this;
}
template<class T>
T& Array<T>::operator[](const size_t index) {
if (index < low || index > high || size <= 0) {
std::cout << "The index " << index << " goes out of bounds.\n";
exit(1);
}
return elements[index - low];
}
template<class X>
std::ostream& operator<<(std::ostream& os, const Array<X>& someArray) {
if (someArray.elements) {
for (size_t i = 0; i < someArray.size - 1; ++i) {
os << someArray.elements[i] << ", ";
}
os << someArray.elements[someArray.size - 1];
} else {
os << 0;
}
}
#endif
// Matrix.h
#include "Array.h"
template<class T>
class Matrix {
private:
Array< Array<T>* > *matrix;
size_t rowSize, columnSize;
public:
Matrix(const size_t rowSize, const size_t columnSize);
};
template<class T>
Matrix<T>::Matrix(const size_t rowSize, const size_t columnSize) {
matrix = new Array< Array<T>* >(rowSize);
// Compiles perfectly fine, except after I uncomment this line:
// matrix[0] = new Array<T>(columnSize);
this->rowSize = rowSize;
this->columnSize = columnSize;
}
matrix isn't an Array<Array<T>*>. It's an Array<Array<T>*>* -- a pointer to an Array<Array<T>*>. This means that matrix[0] (better written as *matrix; see below) is of type Array<Array<T>*>.
You are allocating matrix via new rather than new[]. Only one object is being allocated. You can use matrix[0] to refer to this single object, but since you are allocating with new, it's better to use the dereference operator. Using matrix[0] might lead you or a reader of your code to think there might be a matrix[1]. There isn't.
The way to fix that commented-out statement is to use
(*matrix)[0] = new Array<T>(columnSize);
and if you are allocating in a loop,
(*matrix)[idx] = new Array<T>(columnSize);
Note that referencing the i,j element of the matrix is done via (*matrix)[i][j].
If you insist on using matrix[0] you will need to use
matrix[0][idx] = new Array<T>(columnSize);
to allocate and matrix[0][i][j] to reference the i,j element of the matrix.
Related
I have a set of code that will read in ints from a text file and put it in some matrix object.
fstream& operator>>(fstream& file, DynamicMatrix& matrixIn) {
string rowAndColLine;
string dataline;
char atofIsDumb[1] = { 0 };
getline(file, rowAndColLine);
atofIsDumb[0] = rowAndColLine[0]; //Rows
matrixIn.rows = atoi(atofIsDumb);
atofIsDumb[0] = rowAndColLine[2]; //Cols
matrixIn.cols = atoi(atofIsDumb);
matrixIn.matrix = new double* [matrixIn.rows];
for (int i = 0; i < matrixIn.rows; i++)
{
matrixIn.matrix[i] = new double[matrixIn.cols];
for (int j = 0; j < matrixIn.cols; j++)
{
atofIsDumb[0] = dataline[j];
matrixIn.matrix[i][j] = atof(atofIsDumb);
}
}
return file;
}
I want to modify this function so when I call the operator it will read in a double properly. The way I am reading ints is assuming that every increment of dataline is a new int. However that wont work for doubles. How can I fix this function so it will treat floating point numbers in a text file correctly?
Example I/O
4 2
1.35477 8.35009
9.68868 2.21034
3.08167 5.47221
1.88382 9.92881
2 3
9.96461 9.67695 7.25839
9.8111 1.09862 7.98106
The first line is always row col size, and then the next set of ints is for the next matrix. For readin multiple matrices I lock the file for reading and share the reading among two threads.
The usage of "atoX" is not the way to go. Here you can and should use classical formatted input functions using operator >>.
Then everything can be fairly easy.
Below you can see one of many many potential implementations. This could be used as a basic framework to build a class with more functionalities.
We will provide functions to allocate a 2d array and to free the memory later. ALso the inserter and extraction functions >> and << will be overloaded. And finally we add the rule of 5 special functions. With that, we can store all read data blocks in a std::vector.
Example:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct DynamicMatrix {
// Basic data of a matrix
double** matrix{};
size_t numberOfRows{};
size_t numberOfColumns{};
// We do not want to create memory holes
void clear() {
for (size_t row{}; row < numberOfRows; ++row)
delete[] matrix[row];
delete[] matrix;
numberOfRows = {}; numberOfColumns = {};
}
// Simple extraction operator. Will read only one block of doubles from the stream
friend std::istream& operator >> (std::istream& is, DynamicMatrix& dm) {
// Free old memory and get number of rows and columns for this block
dm.clear();
is >> dm.numberOfRows >> dm.numberOfColumns;
// Get memory for row pointers
dm.matrix = new double* [dm.numberOfRows]{};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < dm.numberOfRows; ++row) {
dm.matrix[row] = new double[dm.numberOfColumns]{};
// And now read the column values
for (size_t col{}; col < dm.numberOfColumns; ++col)
is >> dm.matrix[row][col];
}
return is;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const DynamicMatrix& dm) {
for (size_t row{}; row < dm.numberOfRows; ++row) {
for (size_t col{}; col < dm.numberOfColumns; ++col)
os << dm.matrix[row][col] << '\t';
os << '\n';
}
return os;
}
// Rule of 5
// Default constructor (Elements are already initialized)
DynamicMatrix() {}
// Copy constructor
DynamicMatrix(const DynamicMatrix& dm) {
// Copy the size
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
// Get memory for row pointers
matrix = new double* [numberOfRows]{};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < numberOfRows; ++row) {
matrix[row] = new double[numberOfColumns]{};
// And now copy the data
for (size_t col{}; col < numberOfColumns; ++col)
matrix[row][col] = dm.matrix[row][col];
}
}
// Copy assignment
DynamicMatrix& operator = (const DynamicMatrix& dm) {
if (this != &dm) { // not a self-assignment
// Get rid of old values
clear();
// Copy the size
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
// Get memory for row pointers
matrix = new double* [numberOfRows] {};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < numberOfRows; ++row) {
matrix[row] = new double[numberOfColumns] {};
// And now copy the data
for (size_t col{}; col < numberOfColumns; ++col)
matrix[row][col] = dm.matrix[row][col];
}
}
return *this;
}
// Move constructor
DynamicMatrix(DynamicMatrix&& dm) noexcept {
// Copy size and data
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
matrix = dm.matrix;
// Clear other matrix
dm.matrix = {};
dm.numberOfRows = {};
dm.numberOfColumns = {};
}
// Move assignment
DynamicMatrix& operator = (DynamicMatrix&& dm) noexcept{
if (this != &dm) { // not a self-assignment
clear();
// Copy size and data
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
matrix = dm.matrix;
// Clear other matrix
dm.matrix = {};
dm.numberOfRows = {};
dm.numberOfColumns = {};
}
return *this;
}
// Delete memory when going out of existence
virtual ~DynamicMatrix() {
clear();
}
};
// Testdata
std::istringstream iss{ R"(4 2
1.35477 8.35009
9.68868 2.21034
3.08167 5.47221
1.88382 9.92881
2 3
9.96461 9.67695 7.25839
9.8111 1.09862 7.98106
)" };
// Test function
int main() {
// Here we will store all dynamic matrices
std::vector<DynamicMatrix> blocks{};
// One temporary matrix
DynamicMatrix dmTemp{};
// Read all block from stream
while (iss >> dmTemp) {
blocks.push_back(std::move(dmTemp));
}
// Debug output
for (const DynamicMatrix& block : blocks) {
std::cout << "\n\n" << block;
}
}
If you want to dive in deeper, I show you also a full implementation of a 1d DynamicArray, from which you can of course also build a 2d Matrix by defining an array of arrays.
#include <iostream>
#include <sstream>
#include <initializer_list>
// -----------------------------------------------------------------------------------------------
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
// The Dynamic Array has an initial capacity.
// If more elements will be added, there will be a reallocation with double capacity
static constexpr unsigned int InitialCapacity{ 8 };
// Internal data ------------------------------------------------------------------------------
T* data{}; // Dynamic Storage for Data
unsigned int numberOfElements{}; // Number of elements currently in the container
unsigned int capacity{ InitialCapacity }; // Current maximum capacity of the container
public:
// Construction and Destruction ---------------------------------------------------------------
DynamicArray(); // Default constructor. Allocate new memory
DynamicArray(const unsigned int size); // Constructor for a given size. Allocate new memory
DynamicArray(const DynamicArray& other); // Copy constructor. Make a deep copy
DynamicArray(DynamicArray&& other); // Move constructor
// Special constructors
template <class Iterator> DynamicArray(Iterator begin, Iterator end); // Initialize from range
template <int N> DynamicArray(const T(&other)[N]); // Initialize from C_Sytle array,e.g. a string literal
template <int N> DynamicArray(T(&other)[N]);
DynamicArray(const std::initializer_list<T>& list); // Take data from initializer list
~DynamicArray(); // Destructor: Release previously allocated memory
// Housekeeping ---------------------------------------------------------------
bool empty() const; // Do we have elements in the container? Do not mix up with capacity
void clear(); // Clear will not delete anything. Just set element count to 0
unsigned int size() const; // How many elements are in the container
// Main working functions
void push_back(const T& d); // Add a new element at the end
// Operators for class------------------------ ---------------------------------------------------------------
T operator[] (const unsigned int i) const; // Index operator, get data at given index. No boundary check
T& operator[] (const unsigned int i); // Index operator, get data at given index. No boundary check
DynamicArray& operator=(const DynamicArray& other); // Assignment
DynamicArray& operator=(DynamicArray&& other); // Move Assignment
// Add iterator properties to class ---------------------------------------------------------------
class iterator { // Local class for iterator
T* iter{}; // This will be the iterator
T* begin{}; // For boundary check
T* end{}; // For boundary check
public: // Define alias names necessary for the iterator functionality
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator(T* const i, T* const b, T* const e);
// Dereferencing
reference operator *() const;
pointer operator ->() const;
// Aithmetic operations
iterator& operator ++();
iterator& operator --();
iterator operator ++(int);
iterator operator --(int);
iterator operator +(const difference_type& n) const;
iterator& operator +=(const difference_type& n);
iterator operator -(const difference_type& n) const;
iterator& operator -=(const difference_type& n);
// Comparison
bool operator ==(const iterator& other) const { return iter == other.iter; };
bool operator !=(const iterator& other) const { return iter != other.iter; };
bool operator < (const iterator& other) const { return other.iter - iter < 0; };
bool operator <= (const iterator& other) const { return other.iter - iter <= 0; };
bool operator > (const iterator& other) const { return other.iter - iter > 0; };
bool operator >= (const iterator& other) const { return other.iter - iter >= 0; };
// Reference and difference
reference operator[] (const difference_type& n);
difference_type operator-(const iterator& other) const;
};
// Begin and end function to initialize an iterator
iterator begin() const;
iterator end() const;
// Working functions dealing with iterators. More may be added
iterator erase(iterator pos);
DynamicArray<T> split();
};
template <typename T>
DynamicArray<T> DynamicArray<T>::split() {
DynamicArray<T> result;
if (numberOfElements > 1) {
const size_t offset = numberOfElements / 2;
result.numberOfElements = numberOfElements - offset;
result.data = data + offset;
result.capacity = result.numberOfElements << 1;
numberOfElements = offset;
}
return result;
}
// Default constructor. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray() {
data = new T[capacity];
}
// Constructor for certain size. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray(const unsigned int size) : data(new T[size]), numberOfElements(0), capacity(size) {
}
// Copy constructor
template <typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& other) { // Copy constructor. Make a deep copy
capacity = numberOfElements = other.numberOfElements;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < other.numberOfElements; ++k)
data[k] = other.data[k]; // Copy data
}
// Move constructor
template <typename T>
DynamicArray<T>::DynamicArray(DynamicArray&& other) {
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
// Range constructor
template <typename T>
template <class Iterator>
DynamicArray<T>::DynamicArray(Iterator begin, Iterator end) {
data = new T[capacity];
for (Iterator i = begin; i != end; ++i)
push_back(*i);
}
// Construct from a const C-Style Array, like for example "Hello"
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(const T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from a C-Style Array
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from an initializer list
template <typename T>
DynamicArray<T>::DynamicArray(const std::initializer_list<T>& list) {
data = new T[capacity];
for (const T& t : list) push_back(t);
}
// Destructor will release the dynamic allocated memory
template <typename T>
inline DynamicArray<T>::~DynamicArray() {
delete[] data;
} // Destructor: Release previously allocated memory
// Some houskeeping functions
template <typename T>
inline bool DynamicArray<T>::empty() const {
return numberOfElements == 0;
}
template <typename T>
inline void DynamicArray<T>::clear() {
numberOfElements = 0;
}; // Clear will not delete anything. Just set element count to 0
template <typename T>
inline unsigned int DynamicArray<T>::size() const {
return numberOfElements;
} // How many elements are in the container
// Main workhorse for a dynamic array.
// Store element, and alwaysprovide enough memory
template <typename T>
void DynamicArray<T>::push_back(const T& d) { // Add a new element at the end
if (numberOfElements >= capacity) { // Check, if capacity of this dynamic array is big enough
capacity *= 2; // Obviously not, we will double the capacity
T* temp = new T[capacity]; // Allocate new and more memory
for (unsigned int k = 0; k < numberOfElements; ++k)
temp[k] = data[k]; // Copy data from old memory to new memory
delete[] data; // Release old memory
data = temp; // And assign newly allocated memory to old pointer
}
data[numberOfElements++] = d; // And finally, store the given data at the end of the container
}
// Operators for class ------------------------ ---------------------------------------------------------------
template <typename T>
inline typename T DynamicArray<T>::operator[] (const unsigned int i) const {
return data[i];
} // Index operator, get data at given index. No boundary check
template <typename T>
inline typename T& DynamicArray<T>::operator[] (const unsigned int i) {
return data[i];
} // Index operator, get data at given index. No boundary check
// Assignement operator. Make a deep copy
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(const DynamicArray& other) {
if (this != &other) { // Prevent self-assignment
delete[] data; // Release any previosly existing memory
capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
data = new T[capacity]; // Get new memory, depending on size of other
for (unsigned int k = 0; k < numberOfElements; ++k) // Copy other data
data[k] = other.data[k];
}
return *this;
}
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(DynamicArray&& other) { // Move Assignment
if (this != &other) { // Prevent self-assignment
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
return *this;
}
// Implementation of iterator functions ---------------------------------------------------------------------
// COnstruction
template <typename T>
inline DynamicArray<T>::iterator::iterator(T* const i, T* const b, T* const e) : iter(i), begin(b), end(e) {
}; // Constructor for the iterator
// Dereferencing
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator *() const {
return *iter;
}
template <typename T>
inline typename DynamicArray<T>::iterator::pointer DynamicArray<T>::iterator::operator ->() const {
return iter;
}
// Arithmetic operations
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator ++() {
if (iter < end)
++iter;
return *this;
}
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator --() {
if (iter > begin)
--iter;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator ++(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter < end)
++(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator --(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter > begin)
--(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator +(const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++tmp;
else
while (k++)
--tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator +=(const DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++* this;
else
while (k++)
--* this;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator- (const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--tmp;
else
while (k++)
++tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator -=(const typename DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--* this;
else
while (k++)
++* this;
return *this;
}
// Index operator for iterator
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator[] (const typename DynamicArray<T>::iterator::difference_type& n) {
return *(iter + n);
};
template <typename T>
typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator-(const iterator& other) const {
difference_type result{};
int indexThis{ -1 }, indexOther{ -1 }, index{};
do {
;
if (nptr == iter)
indexThis = index;
if (nptr == other.iter)
indexOther = index;
++index;
} while (nptr != head);
if (indexThis >= 0 and indexOther >= 0)
result = indexThis - indexOther;
return result;
}
// Delta
template <typename T>
inline typename DynamicArray<T>::iterator::difference_type DynamicArray<T>::iterator::operator-(const typename DynamicArray<T>::iterator& other) const {
return iter - other.iter;
}
// ------------------------------------------------------------------------
// Get iterators for dynamic array
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::begin() const {
return iterator(data, data, data + numberOfElements);
}
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::end() const {
return iterator(data + numberOfElements, data, data + numberOfElements);
}
// ------------------------------------------------------------------------
// Any other functions for dynamic array
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::erase(typename DynamicArray<T>::iterator pos) {
iterator result{ pos };
if (pos != end()) {
while (pos != end()) {
*pos = *(pos + 1);
++pos;
}
++result;
--numberOfElements;
}
return result;
}
int main() {
DynamicArray<int> da1{ 1,2,3,4,5,6 };
DynamicArray<int> da2 = da1.split();
for (const int i : da1)
std::cout << i << ' ';
std::cout << "\n\n";
for (const int k : da1)
std::cout << k << ' ';
}
#include <algorithm>
#include <utility>
#include <new>
#include <iostream>
template <typename T>
class Vector {
public:
Vector();
~Vector();
void push_back(const T& value);
void push_back(T&& value);
void clear();
std::size_t size() const { return sz; }
std::size_t capacity() const { return cap; }
T& operator[](std::size_t i) { return data[i]; }
const T& operator[](std::size_t i) const { return data[i]; }
private:
T* data;
std::size_t sz;
std::size_t cap;
void p_realloc(std::size_t n);
};
template <typename T>
Vector<T>::Vector()
: sz{}, cap{ 10 }
{
data = (T*)::operator new(cap * sizeof(T));
}
template <typename T>
Vector<T>::~Vector() {
clear();
::operator delete(data, sizeof(T) * cap);
}
template <typename T>
void Vector<T>::p_realloc(std::size_t n) {
T* new_data = (T*)::operator new(n * sizeof(T));
if (n < sz)
sz = n;
for (std::size_t i = 0; i < sz; ++i) {
new_data[i] = std::move(data[i]);
data[i].~T();
}
::operator delete(data, cap * sizeof(T));
data = new_data;
cap = n;
}
template <typename T>
void Vector<T>::clear() {
for (std::size_t i = 0; i < sz; ++i)
data[i].~T();
sz = 0;
}
template <typename T>
void Vector<T>::push_back(const T& value) {
if (sz >= cap)
p_realloc(cap * 1.5);
data[sz++] = value;
}
template <typename T>
void Vector<T>::push_back(T&& value) {
if (sz >= cap)
p_realloc(cap * 1.5);
std::cout << "All good\n"; std::cin.get();
data[sz++] = std::move(value);
}
I am trying to create a custom implementation of Vector class. I have already made one but it does not use ::operator new and delete in order to not call the constructor/destructor. When T=std::string and i try to call push_back("test_string") i get an error and i cannot figure about why. Should it not be implicitly converted to std::string and therefore data[sz++] = std::move(value) work ?
The error is not in the using push_back(T&&). The reason is in the data that points to an uninitialized memory.
data[sz++] = value; calls T::operator=(const T&) on an uninitialized object T.
data[sz++] = std::move(value); calls T::operator=(T&&) on an uninitialized object T.
You should fix assignments data[sz++] = in the both push_back with using the placement new:
/* data[sz++] = value; */ new (&data[sz++]) T(value);
/* data[sz++] = std::move(value); */ new (&data[sz++]) T(std::move(value));
I am learning C++ and as a program minimum wish to build a few elementary data structures(arrays, vectors, lists, stacks, queues, matrices etcetera) using generic programming. Now, while classes for data containers are already defined in the C++ STL, I would like to learn by coding simple containers of my own.
To that end, I created QPArray class. QPArray data objects are fixed sized arrays. QPStack should accept any data container, that will be the home of the stack. Just like in STL.
I have tested QPArray and the code works well for integers.
1) I was searching the internet to see, how I could list initialize my own QPArray like below. However, I was unable to find any references.
QPArray<int,5> arr= {2,3,5,7,8};
Any tips or links would be immmensely helpful.
2) I tried to create a QPArray of std::string's. I tried printing the one of the elements to the console, but the correct version of the operator << () could not be found. I am under the assumption, that << is overloaded to work with std::string objects as arguments. Would anyone know, why this fails?
TestQPArray.cpp
QPArray<std::string, 7> arr1;
arr1[0] = "C++";
arr1[1] = "for";
arr1[2] = "quantitative";
arr1[3] = "finance";
arr1[4] = "is";
arr1[5] = "absolutely";
arr1[6] = "awesome";
std::cout << arr1.at(3) << std::endl;//Compile error
I apologize for posting the entire list QPArray.h, but it's need for completeness.
QPArray.h
#ifndef QPARRAY_H
#define QPARRAY_H
#include <cstring>
template <class T, std::size_t N>
class QPArray
{
private:
T* items;
int maxsize;
public:
/*Constructors and destructors*/
QPArray();
QPArray(const T value);
QPArray(const QPArray<T, N>& a);
virtual ~QPArray();
/* Overload = operator*/
QPArray<T, N>& operator =(const QPArray<T, N>& a);
/* Selectors - Element access*/
T& at(int index);
T& operator [](int index);
T& front();
T& back();
/* Capacity*/
bool empty();
virtual int size();
virtual int max_size();
/*Operations*/
QPArray<T, N>& fill(T value);
};
template <class T,std::size_t N>
QPArray<T, N>::QPArray()
{
if (N > 0)
{
items = new T[N];
}
maxsize = N;
}
template <class T, std::size_t N>
QPArray<T, N>::QPArray(T value)
{
maxsize = N;
if (N > 0)
{
items = new T[N];
for (int i = 0; i < maxsize; i++)
{
items[i] = value;
}
}
}
template <class T, std::size_t N>
QPArray<T,N>::QPArray(const QPArray<T, N>& a)
{
maxsize = a.maxsize;
items = new T[maxsize];
for (int i = 0; i < maxsize; i++)
items[i] = a.items[i];
}
template<class T, std::size_t N>
QPArray<T, N>::~QPArray()
{
if(N > 0)
delete[] items;
}
template<class T, std::size_t N>
QPArray<T, N>& QPArray<T, N>::operator =(const QPArray<T, N>& a)
{
if (this == &a)
return *this;
if (maxsize != a.maxsize)
throw std::runtime_error(std::string("Src and target array bounds do not match"));
for (int i = 0; i < maxsize; i++)
{
items[i] = a.items[i];
}
return *this;
}
template <class T, std::size_t N>
T& QPArray<T, N>::at(int i)
{
return items[i];
}
template <class T, std::size_t N>
T& QPArray<T, N>::operator [](int index)
{
return items[index];
}
//Access the first element of the array
template <class T, size_t N>
T& QPArray<T, N>::front() {
return items[0];
}
//Access the last element of the array
template <class T, std::size_t N>
T& QPArray<T, N>::back() {
return items[maxsize - 1];
}
template <class T, std::size_t N>
bool QPArray<T, N>::empty()
{
if (maxsize == 0)
return true;
else
return false;
}
template <class T, std::size_t N>
int QPArray<T,N>::size()
{
return maxsize;
}
template <class T, std::size_t N>
int QPArray<T, N>::max_size()
{
return maxsize;
}
template <class T, std::size_t N>
QPArray<T, N>& QPArray<T, N>::fill(T value)
{
for (int i = 0; i < maxsize; i++)
{
items[i] = value;
}
return *this;
}
#endif // !ARRAY_H
Ad 1 Try with std::initializer_list. It enables the container to assigned a {a, b, c, d} to the constructed variable.
For example
template <class T, std::size_t N>
QPArray<T,N>::QPArray(std::initializer_list<T> v)
{
maxsize = v.size();
items = new T[maxsize];
int i = 0;
for (auto & elem: v)
items[i++] = elem;
}
Ad 2 You haven't included string library in the file, as #Bo Persson indicated.
I'm having some problem with my std::forward constructor for my template "matrix" class. Basically i want to set a matrix of type float and size 4 equal to the sum of 2 matrices of type float and size 3. I do this inside of my struct 'matrix_struct' in the function 'test'. However, MSVC error tells me that "'static_cast': cannot convert from 'matrix' to 'float'" and whenever I inspect the error it takes me to the 3rd matrix constructor with std::forward.
///////////////////////////////////
somefile.hpp
#pragma once
#include "matrix.hpp"
using matrix3 = matrix<float, 3>;
using matrix4 = matrix<float, 4>;
struct matrix_struct {
matrix4 sum;
void test(const matrix3& a, const matrix3& b)
{
sum = a + b;
}
}
///////////////////////////////////
matrix.hpp
#pragma once
#include <array>
template <typename t, size_t dim>
class matrix
{
public:
matrix() { data.fill(static_cast<t>(0) }
explicit matrix(const std::array<t, dim>& a) : data(a) {}
template <typename... args_t>
matrix(args_t... args) : data{ static_cast<t>(std::forward<args_t>(args))... } }
public:
t& at(const size_t index)
{
return data.at(index >= dim ? dim - 1 : index);
}
const t& at(const size_t index) const
{
return data.at(index >= dim ? dim - 1 : index);
}
public:
matrix& operator = (const matrix<t, dim>& other)
{
for (size_t i = 0; i < dim; ++i) {
at(i) = other.at(i);
}
return *this;
}
matrix& operator = (const std::array<t, dim>& other)
{
for (size_t i = 0; i < dim; ++i) {
at(i) = other.at(i);
}
return *this;
}
matrix& operator = (const t& other)
{
for (size_t i = 0; i < dim; ++i) {
at(i) = other;
}
return *this;
}
public:
matrix operator + (const matrix<t, dim>& other) const
{
matrix<t, dim> ret;
for (size_t i = 0; i < dim; ++i) {
ret.at(i) = at(i) + other.at(i);
}
return ret;
}
matrix operator + (const std::array<t, dim>& other) const
{
matrix<t, dim> ret;
for (size_t i = 0; i < dim; ++i) {
ret.at(i) = at(i) + other.at(i);
}
return ret;
}
matrix operator + (const t& other) const
{
matrix<t, dim> ret;
for (size_t i = 0; i < dim; ++i) {
ret.at(i) = at(i) + other;
}
return ret;
}
private:
std::array<t, dim> data;
};
Template constructors are problematic. They often create code that is a better candidate than your other constructors.
The general solution is to disable the template if its decayed type matches the class you are writing.
example:
struct MyClass
{
template
<
class Arg,
class...Rest,
std::enable_if_t
<
! std::is_same
<
std::decay_t<Arg>,
MyClass
>::value
>* = nullptr
>
MyClass(Arg&& arg, Rest&&...rest)
{
// code to construct from something that's not MyClass
// this will no longer hijack copy constructors etc.
}
};
The first problem of your code sample is addressed by #RichardHodges's answer.
Assuming you include his solution to overcome tricky copy/move constructor selection, another problem remains: you do not offer a matrix promotion/demotion service through your constructors/assignment operators.
Therefore, the following line in your test function:
sum = a + b; // a + b is a matrix<float, 3>, sum a matrix<float, 4>
Will trigger a call to the variadic template constructor and fail.
Starting from Richard's solution, you need to tweak a bit the SFINAE condition to extend it to matrices of any size. To do so, we will need a little is_matrix trait:
template <typename T, size_t Dim>
class matrix;
template <typename T>
struct is_matrix : std::false_type {};
template <typename Num, size_t Size>
struct is_matrix<matrix<Num, Size> > : std::true_type {
using value_type = Num;
};
Now the variadic template constructor becomes:
template <typename t, size_t dim>
class matrix
{
/* ... */
public:
/* ... */
template
<
class Arg,
class...Rest,
std::enable_if_t
<
! std::is_matrix
<
std::decay_t<Arg>
>::value
>* = nullptr
>
matrix(Arg&& arg, Rest&&...rest)
{
// code to construct from something that's not a matrix
// this will no longer hijack copy constructors etc.
}
};
Then, we need to add the proper matrix constructor along with the proper friend declaration:
template <typename t, typename dim>
class matrix {
public:
template <typename OtherT, size_t OtherDim>
friend class matrix;
template <size_t OtherDim>
matrix(matrix<t, OtherDim> const& other) {
size_t i = 0;
for (; i < min(OtherDim, dim); ++i) {
data[i] = other.data[i];
}
for(; i < dim; ++i) {
data[i] = t();
}
}
template <typename OtherT,
size_t OtherDim>
matrix(matrix<OtherT, OtherDim> const&) {
static_assert(std::is_same<t, OtherT>::value,
"value_type mismatch between matrices!");
}
/* ... */
};
Note: You need the friend declaration because matrix<Type1, Dim1> and matrix<Type2, Dim2> are completely different types whenever Type1 != Type2 or Dim1 != Dim2 and as such, you cannot access matrix<OtherT, OtherDim>'s private/protected members in matrix<t, dim> without that friend declaration.
This implementation will initialize the target matrix by filling its data member with the content of the given matrix when the value types match:
If the given matrix is bigger, it will be truncated.
If the given matrix is smaller, the remaining elements will be 0 initialized
If the value types don't match, the less specialized matrix<OtherT, OtherDim> constructor is the only available overload and it triggers a compiler error through a static_assert.
You would also need to define the equivalent assigment operators... Which I left as exercises.
A demo of these constructors in action can be found on Coliru
I'm trying to write a class View to serve as a view into another container, (a sparse matrix class, but that should be unimportant for the question).
View should contain references (e.g. std::reference_wrapper) to a selection of elements in the container, and have methods returning references to those elements, as well as an assignment operator making one block equal to another.
My problem is that I want View to be able to take values in addition to references: both be constructed from values as a non-reference instance to be used in assignments, and assign values to single elements in a reference instance.
An MVE of the code so far is:
#include <array>
template<typename T, size_t size>
class View
{
private:
std::array<T, size> _values;
public:
View(const std::array<T, size> & values)
: _values{ values } { }
// ----------
View<T, size> & operator=(const View<T, size> & other)
{
for ( size_t i = 0; i < size; ++i ) {
this->get(i) = other.get(i);
}
return *this;
}
// ----------
T & get(size_t idx)
{
return _values.at(idx);
}
const T & get(size_t idx) const
{
return _values.at(idx);
}
};
It can be used like this:
#include <functional>
#include <iostream>
int main()
{
int values[5] = { 1, 2, 3, 4, 5 };
View<int, 2> v1{
{values[0], values[1]}
};
View<std::reference_wrapper<int>, 2> v2{
{values[3], values[4]}
};
// WHAT WORKS
v1.get(0) = 10; // can assign to the non reference `View<int, size>`,
// works as intended
v2.get(0) += 9; // can increment through the reference wrappers,
// this also works as intended
// WHAT THAT DOES NOT WORK
// v2 = v1; // nether of these work, as there is no conversion
// v1 = v2; // between `View<std::reference_wrapper<int>, size>`
// and `View<int, size>`. It is the first expression
// that is of most interest
// v2.get(1) = 10; // this doesn't work as the return is a
// `std::reference_wrapper<int>`, not a
// reference to an `int`
v2.get(1).get() = 10; // this works as a work-around to
// this problem, but it feels clunky, and it
// makes the interface between the two types
// different
for ( size_t i = 0; i < 2; ++i ) {
std::cout << v1.get(i) << " ";
}
std::cout << std::endl;
for ( size_t i = 0; i < 5; ++i ) {
std::cout << values[i] << " ";
}
std::cout << std::endl;
}
This should output:
10 2
1 2 3 13 10
I'm using clang++ to compile on Ubuntu 15.10.
So specifically,
How should I implement the assignment operator to allow View<T, size> and View<std::reference_wrapper<T>, size> to be assigned to each other (or at least the former to be assigned to latter). Creating two versions
View<T, size> & operator=(const View<T, size> & other);
View<T, size> & operator=(
const View<std::reference_wrapper<T>, size> & other);
does not work, (as a View<std::reference_wrapper<T>, size> then would need a View<std::reference_wrapper<std::reference_wrapper<T> >, size> for the second overload).
How can I write the get(size_t idx) methods such that the return is T & for bothView<T, size> and View<std::reference_wrapper<T>, size>?
I have a feeling this can be accomplished by using templates somehow, but I'm still quite new to template programming so I'm a bit lost.
Here is a way to make get() return T& for T and std::reference_wrapper<T>:
template <typename T>
struct get_value_type {
using type = T;
};
template <typename T>
struct get_value_type<std::reference_wrapper<T>> {
using type = T;
};
template<typename T, size_t size>
class View {
using value_type = typename get_value_type<T>::type;
value_type & get(size_t idx) {
return _values.at(idx);
}
const value_type & get(size_t idx) const {
return _values.at(idx);
}
};
The get_value_type template help us obtain T from both T and std::reference_wrapper<T>, then you simply change the return type of get() to value_type, and since std::reference_wrapper<T> is implicitly convertible to T& it works.
Now that you have access to value_type, you can use it to create your two operator=:
View& operator= (const View<value_type, size> & other) {
for (size_t i = 0; i < size; ++i) {
this->get(i) = other.get(i);
}
return *this;
}
View& operator=(const View<std::reference_wrapper<value_type>, size> & other) {
for (size_t i = 0; i < size; ++i) {
this->get(i) = other.get(i);
}
return *this;
}
In case you want to allow assignment from different view (e.g. a view of int to a view of double), you could use a templated version:
template <typename U>
View<T, size> & operator=(const View<U, size> & other) {
for (size_t i = 0; i < size; ++i) {
this->get(i) = other.get(i);
}
return *this;
}
A small addition that may be a bit off-topic but instead of having an std::array attribute, you could inherit from std::array like the following:
template<typename T, size_t Size>
struct View: public std::array<T, Size> {
using array_type = std::array<T, Size>;
using value_type = typename get_value_type<T>::type;
View (std::array<T, Size> const& values) : array_type (values) { }
View& operator=(const View<value_type, Size> & other) {
for (size_t i = 0; i < Size; ++i) {
(*this)[i] = other[i];
}
return *this;
}
View& operator=(const View<std::reference_wrapper<value_type>, Size> & other) {
for (size_t i = 0; i < Size; ++i) {
(*this)[i] = other[i];
}
return *this;
}
value_type & operator[](size_t idx) {
return array_type::operator[](idx);
}
const value_type & operator[](size_t idx) const {
return array_type::operator[](idx);
}
};
This would allow you to use a lot of stuff from the standard library on your View without having to redefine anything.