Если у меня есть двумерный массив фиксированного размера T array[M][N]
,
Я могу получить доступ к элементам через array[i][j]
и assert(sizeof(array[i]) == N * sizeof(T))
.
Как я могу сделать это для массива, завернутого в структуру?
Следующий код позволяет получать доступ к элементам через array[i][j]
, но не выполняет утверждение.
Есть ли способ operator[]
вернуть T[N]
вместо T*
?
template<class T, int M, int N>
struct Array
{
T data[M][N];
// constexpr T[N] operator[] (int i) - CANNOT RETURN T[N]!
constexpr T* operator[] (int i)
{
return data[i];
}
}
Вы можете вернуть std::span
во внутренний массив.
Вот почему C++ как std::array
Примечание для будущих читателей: помните, что sizeof
в указателе всегда указывает размер указателя, а не того, на что он указывает. Указатель знает только, где что-то может быть. Он не знает, действительно ли существует действительный объект или этот объект является массивом.
Возврат ссылки на массив дает sizeof
, который вы искали:
template<class T, int M, int N>
struct Array
{
T data[M][N];
// constexpr T[N] operator[] (int i) - CANNOT RETURN T[N]!
constexpr T (&operator[](int i))[N]
{
return data[i];
}
};
int main()
{
Array<double, 2, 3> a;
std::cout << sizeof(a) << ", " << sizeof(a[1]);
}
Введение псевдонима типа облегчает чтение.
using row = T[N]; // or typedef T (&row)[N]
constexpr row& operator[](int i)
{
return data[i];
}
Или вы можете сделать обертку вот так: (в целом в C++ нет необходимости так часто использовать sizeof, особенно для проверки размеров массива)
#include <array>
#include <stdexcept>
#include <iostream>
template<typename T, std::size_t ROWS, std::size_t COLS>
class array2d_t final
{
public:
array2d_t() = default;
~array2d_t() = default;
array2d_t(const array2d_t&) = default;
array2d_t& operator=(const array2d_t&) = default;
array2d_t(array2d_t&&) = default;
array2d_t& operator=(array2d_t&&) = default;
explicit array2d_t(const int (&values)[ROWS][COLS])
{
std::size_t offset{0};
for(std::size_t row{0ul}; row < ROWS; ++row)
{
for(std::size_t col{0ul}; col < COLS; ++col, ++offset)
{
m_values[offset] = values[row][col];
}
}
}
auto& at(std::size_t row, std::size_t col)
{
if ((row >= ROWS) || (col >= COLS)) throw std::invalid_argument{ "index out of range" };
std::size_t offset = (COLS*row) + col;
return m_values[offset];
}
const auto& operator()(std::size_t row, std::size_t col) const noexcept
{
std::size_t offset = (COLS*row) + col;
return m_values[offset];
}
const auto& at(std::size_t row, std::size_t col) const
{
if ((row >= ROWS) || (col >= COLS)) throw std::invalid_argument{ "index out of range" };
return operator()(row,col);
}
constexpr std::size_t rows() const noexcept { return ROWS;};
constexpr std::size_t columns() const noexcept { return COLS;};
private:
std::array<T,ROWS*COLS> m_values;
};
int main()
{
array2d_t<int,2,2> matrix{{{0,1},{2,3}}};
}
Пробовали вернуть ссылку на массив? Что-то вроде
T (&operator[](int i))[N]
Он не возвращает значение, ноT*
также не копирует содержимое среза.