简体   繁体   中英

How to implement bound checking for std::array?

I have extended the C++ 11 std::array , it is working file, but when I try to overload the operator[] , I got this error:

 error: lvalue required as left operand of assignment
 array[0] = 911;
              ^~~

Is it possible to implement the operator[] adding bound checking for the std::array type?

This is the code:

#include <array>
#include <cassert>
#include <iostream>

template <unsigned int array_size, typename array_datatype=long int>
struct Array : public std::array<array_datatype, array_size>
{
  Array()
  {
  }

  // std::array constructor inheritance
  // https://stackoverflow.com/questions/24280521/stdarray-constructor-inheritance
  Array(std::initializer_list< array_datatype > new_values)
  {
    unsigned int data_size = new_values.size();
    unsigned int column_index = 0;
    // std::cout << data_size << std::endl;

    if( data_size == 1 )
    {
      this->clear(*(new_values.begin()));
    }
    else
    {
      assert(data_size == array_size);

      for( auto column : new_values )
      {
        (*this)[column_index] = column;
        column_index++;
      }
    }
  }

  array_datatype operator[](unsigned int line)
  {
    assert(line < array_size);
    assert(line > -1);
    return (*this)[line];
  }

  /**
   * Prints a more beauty version of the array when called on `std::cout<< array << std::end;`
   */
  friend std::ostream& operator<<( std::ostream &output, const Array &array )
  {
    unsigned int column;
    output << "{";

    for( column=0; column < array_size; column++ )
    {
      output << array[column];

      if( column != array_size-1 )
      {
        output << ", ";
      }
    }

    output << "}";
    return output;
  }
}

Related:

  1. Is it possible to enable array bounds checking in g++?
  2. Accessing an array out of bounds gives no error, why?

If you want to use the return value of operator[] on the left side of an assignment, you have to return the array element by reference, not by value.

You also have a recursive loop, as you are calling your own operator[] from inside of itself. You want to call the base class's operator[] instead, so you need to qualify it.

Try this:

array_datatype& operator[](unsigned int line)
{
    assert(line < array_size);
    assert(line > -1);
    return std::array<array_datatype, array_size>::operator[](line);
}

You can use:

array_datatype& operator[](unsigned int line)&
array_datatype const& operator[](unsigned int line)const&
array_datatype operator[](unsigned int line)&&

If you want bound checked access to array elements, just use the at method of std::vector (or std::array ) instead of the [] operator. It is there for that purpose, don't reinvent the wheel :).

See reference for documentation of array bounds checking.

This was my solution for single dimensional arrays:

#include <array>
#include <cassert>
#include <iostream>

template <unsigned int array_width, typename array_datatype=long int>
struct Array
{
  /**
   * Is it okay to inherit implementation from STL containers, rather than delegate?
   * https://stackoverflow.com/questions/2034916/is-it-okay-to-inherit-implementation-from-stl-containers-rather-than-delegate
   */
  std::array<array_datatype, array_width> _data;

  /**
   * std::array constructor inheritance
   * https://stackoverflow.com/questions/24280521/stdarray-constructor-inheritance
   */
  Array()
  {
  }

  Array(std::initializer_list< array_datatype > new_values)
  {
    unsigned int data_size = new_values.size();
    unsigned int column_index = 0;
    // std::cout << data_size << std::endl;

    if( data_size == 1 )
    {
      this->clear(*(new_values.begin()));
    }
    else
    {
      assert(data_size == array_width);

      for( auto column : new_values )
      {
        this->_data[column_index] = column;
        column_index++;
      }
    }
  }

  /**
   * Overloads the `[]` array access operator, allowing you to access this class objects as the
   * where usual `C` arrays.
   *
   * How to implement bound checking for std::array?
   * https://stackoverflow.com/questions/49419089/how-to-implement-bound-checking-for-stdarray
   *
   * @param  line the current line you want to access
   * @return      a pointer to the current line
   */
  array_datatype operator[](unsigned int line)&&
  {
    assert(line < array_width);
    assert(line >= 0);
    return this->_data[line];
  }

  array_datatype const& operator[](unsigned int line)const&
  {
    assert(line < array_width);
    assert(line >= 0);
    return this->_data[line];
  }

  array_datatype& operator[](unsigned int line)&
  {
    assert(line < array_width);
    assert(line >= 0);
    return this->_data[line];
  }

  void clear(array_datatype initial = 0)
  {
    unsigned int column_index = 0;

    for( ; column_index < array_width; column_index++ )
    {
      this->_data[column_index] = initial;
    }
  }

  /**
   * The Array<> type includes the Matrix<> type, because you can multiply a `Array` by an `Matrix`,
   * but not a vice-versa.
   */
  void multiply(Array< array_width, Array< array_width, array_datatype > > &matrix)
  {
    unsigned int column;
    unsigned int step;

    array_datatype old_array[array_width];

    for(column = 0; column < array_width; column++)
    {
      old_array  [column] = this->_data[column];
      this->_data[column] = 0;
    }

    for(column = 0; column < array_width; column++)
    {
      for(step = 0; step < array_width; step++)
      {
        this->_data[column] += old_array[step] * matrix._data[step][column];
      }
    }
    // If you would like to preserve the original value, it can be returned here
    // return old_array;
  }

  /**
   * Prints a more beauty version of the array when called on `std::cout<< array << std::end;`
   */
  friend std::ostream& operator<<( std::ostream &output, const Array &array )
  {
    unsigned int column;
    output << "{";

    for( column=0; column < array_width; column++ )
    {
      output << array._data[column];

      if( column != array_width-1 )
      {
        output << ", ";
      }
    }

    output << "}";
    return output;
  }
};

And this is a extension for matrix (multi-dimensional):

#include <cassert>
#include <iostream>

#include "array.h"

/**
 * C++ Matrix Class
 * https://stackoverflow.com/questions/2076624/c-matrix-class
 *
 * A proper way to create a matrix in c++
 * https://stackoverflow.com/questions/618511/a-proper-way-to-create-a-matrix-in-c
 *
 * error: incompatible types in assignment of 'long int (*)[4]' to 'long int [4][4]'
 * https://stackoverflow.com/questions/49312484/error-incompatible-types-in-assignment-of-long-int-4-to-long-int
 */
template <unsigned int matrix_width=3, unsigned int matrix_height=3, typename matrix_datatype=long int>
struct Matrix : public Array< matrix_height, Array< matrix_width, matrix_datatype > >
{
  Matrix()
  {
  }

  Matrix(matrix_datatype initial)
  {
    this->clear(initial);
  }

  Matrix(std::initializer_list< std::initializer_list< matrix_datatype > > raw_data)
  {
    // std::cout << raw_data.size() << std::endl;
    assert(raw_data.size() == matrix_height);

    // std::cout << raw_data.begin()->size() << std::endl;
    assert(raw_data.begin()->size() == matrix_width);

    unsigned int line_index = 0;
    unsigned int column_index;

    for( auto line : raw_data )
    {
      column_index = 0;

      for( auto column : line )
      {
        this->_data[line_index][column_index] = column;
        column_index++;
      }

      line_index++;
    }
  }

  void clear(matrix_datatype initial=0)
  {
    unsigned int line;
    unsigned int column;

    for( line=0; line < matrix_height; line++ )
    {
      for( column=0; column < matrix_width; column++ )
      {
        this->_data[line][column] = initial;
      }
    }
  }

  void multiply(Matrix &matrix)
  {
    unsigned int line;
    unsigned int column;
    unsigned int step;
    matrix_datatype old_matrix[matrix_height][matrix_width];

    for(line = 0; line < matrix_height; line++)
    {
      for(column = 0; column < matrix_width; column++)
      {
        old_matrix [line][column] = this->_data[line][column];
        this->_data[line][column] = 0;
      }
    }

    for(line = 0; line < matrix_height; line++)
    {
      for(column = 0; column < matrix_width; column++)
      {
        for(step = 0; step < matrix_width; step++)
        {
          this->_data[line][column] += old_matrix[line][step] * matrix._data[step][column];
        }
        // std::cout << "this->_data[line][column] = " << this->_data[line][column] << std::endl;
      }
    }
    // If you would like to preserve the original value, it can be returned here
    // return old_matrix;
  }

  /**
   * Prints a more beauty version of the matrix when called on `std::cout<< matrix << std::end;`
   */
  friend std::ostream& operator<<( std::ostream &output, const Matrix &matrix )
  {
    unsigned int line;
    unsigned int column;
    output << "{";

    for( line=0; line < matrix_height; line++ )
    {
      output << "{";

      for( column=0; column < matrix_width; column++ )
      {
        output << matrix._data[line][column];

        if( column != matrix_width-1 )
        {
          output << ", ";
        }
      }

      if( line != matrix_height-1 )
      {
        output << "}, ";
      }
      else
      {
        output << "}";
      }
    }

    output << "}";
    return output;
  }
};

This is a simple test application for it:

#include "array.h"
#include "matrix.h"

void array_tests();
void matrix_tests();

/**
 * To build it use:
 *     g++ -std=c++11 test.cpp -o main
 */
int main (int argc, char* argv[])
{
  array_tests();

  std::cout << std::endl;
  matrix_tests();
}

// struct Matrixx : public Array< 3, Array< 3, int > >
// {
// };

void array_tests()
{
  std::cout << "Array tests" << std::endl;
  Array<3, long int> array;
  array[1] = 99911;

  std::cout << array << std::endl;
  std::cout << array[1] << std::endl;
  std::cout << array[2] << std::endl;

  Array<3> array2 = {0,0,0};
  std::cout << "array2: " << array2 << std::endl;

  Array<3> array3 = {3};
  std::cout << "array3: " << array3 << std::endl;
}

void matrix_tests()
{
  std::cout << "Matrix tests" << std::endl;
  Matrix<3, 3, long int> matrix;
  std::cout << matrix << std::endl;

  matrix[0][0] = 911;
  std::cout << matrix << std::endl;

  std::cout << matrix[0] << std::endl;
  std::cout << matrix[0][0] << std::endl;

  Matrix<3, 3> matrix2{ {0,0,0}, {0,0,0}, {0,0,0} };
  std::cout << matrix2 << std::endl;

  Matrix<3, 3> matrix3 = { 3 };
  std::cout << matrix3 << std::endl;

  Matrix<3, 1, long int> matrix4 = { 4 };
  std::cout << matrix4 << std::endl;
}

Running it, you would see this:

Array tests
{0, 99911, 0}
99911
0
array2: {0, 0, 0}
array3: {3, 3, 3}

Matrix tests
{{39593264, 0, 1875895727}, {0, 39593264, 0}, {1875566066, 0, -927864272}}
{{911, 0, 1875895727}, {0, 39593264, 0}, {1875566066, 0, -927864272}}
{911, 0, 1875895727}
911
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}
{{3, 3, 3}, {3, 3, 3}, {3, 3, 3}}
{{4, 4, 4}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM