You wrote mSectionsSubsets[i]
with i
from 0
to count
.
Every single one of those accesses is illegal, because mSectionsSubsets
has no elements in it.
Reserving capacity, and resizing the vector, are two different things.
In this particular case, perhaps:
mSectionsSubsets.resize(count);
for (int i = 0; i < count; i++) {
mSectionsSubsets[i].reserve(NUM_SECTIONS);
}
However, overall I would caution against vectors of vectors: they're poison for your cache and just generally not necessary when your dimensions are square.
How about a nice std::vector<int>
instead? If you need count*NUM_SECTIONS
elements, then simply do that. You can always create a two-dimensional façade for its indexes:
i = x + width*y
Cache of a quick mockup from the comments:
#include <iostream>
#include <vector>
#include <stdexcept>
#include <cassert>
// Like a vector<vector<T>>, but *better*!
template <typename T>
class RowList
{
public:
RowList(const std::size_t rowCount, const std::size_t maxRowLength)
: rowCount(rowCount)
, maxRowLength(maxRowLength)
, data(rowCount * maxRowLength)
, utilisation(rowCount)
{}
std::size_t getRowCount() const
{
return rowCount;
}
std::size_t getMaxRowLength() const
{
return maxRowLength;
}
// UB if you give an invalid row number
std::size_t getRowLength(const std::size_t rowNumber) const
{
assert(rowNumber < rowCount);
return utilisation[rowNumber];
}
// UB if you give an invalid row number
void clearRow(const std::size_t rowNumber)
{
assert(rowNumber < rowCount);
utilisation[rowNumber] = 0;
#ifdef NDEBUG
// Debug builds only - make all the dead values -1
// so we can maybe more easily spot misuse
const std::size_t start = rowNumber*maxRowLength;
const std::size_t end = start + maxRowLength;
for (std::size_t i = start; i < end; ++i)
data[i] = -1;
#endif
}
// UB if you give an invalid row number
// throws std::out_of_range if the row is full
void pushToRow(const std::size_t rowNumber, T value)
{
assert(rowNumber < rowCount);
std::size_t& columnNumber = utilisation[rowNumber];
if (columnNumber == maxRowLength)
throw std::out_of_range("Row is full!");
data[rowNumber*maxRowLength + columnNumber] = std::move(value);
columnNumber++;
}
// UB if you give an invalid row or column number
T& elementAt(const std::size_t rowNumber, const std::size_t columnNumber)
{
assert(rowNumber < rowCount);
assert(columnNumber < utilisation[rowNumber]);
return data[rowNumber*maxRowLength + columnNumber];
}
// UB if you give an invalid row or column number
const T& elementAt(const std::size_t rowNumber, const std::size_t columnNumber) const
{
assert(rowNumber < rowCount);
assert(columnNumber < utilisation[rowNumber]);
return data[rowNumber*maxRowLength + columnNumber];
}
private:
const std::size_t rowCount;
const std::size_t maxRowLength;
std::vector<T> data;
std::vector<std::size_t> utilisation;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const RowList<T>& matrix)
{
const auto height = matrix.getRowCount();
for (std::size_t y = 0; y < height; ++y)
{
const auto width = matrix.getRowLength(y);
const auto remainder = matrix.getMaxRowLength() - width;
for (std::size_t x = 0; x < width; ++x)
os << matrix.elementAt(y, x) << '\t';
for (std::size_t i = 0; i < remainder; ++i)
os << "?\t";
os << '\n';
}
return os;
}
int main()
{
RowList<int> matrix(5, 5);
matrix.pushToRow(2, 100);
matrix.pushToRow(2, 101);
matrix.pushToRow(4, 102);
std::cerr << matrix << '\n';
matrix.clearRow(2);
matrix.pushToRow(1, 103);
std::cerr << matrix << '\n';
}
g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
? ? ? ? ?
? ? ? ? ?
100 101 ? ? ?
? ? ? ? ?
102 ? ? ? ?
? ? ? ? ?
103 ? ? ? ?
? ? ? ? ?
? ? ? ? ?
102 ? ? ? ?