【问题标题】:How to implement bound checking for std::array?如何实现 std::array 的边界检查?
【发布时间】:2018-03-22 01:27:37
【问题描述】:

我已经扩展了 C++ 11 std::array,它是工作文件,但是当我尝试重载 operator[] 时,我得到了这个错误:

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

是否可以实现operator[] 为std::array 类型添加边界检查?

这是代码:

#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;
  }
}

相关:

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

【问题讨论】:

  • 您还应该注意comparisons between signed and unsigned,例如您的assert(line &gt; -1);,它几乎肯定不会通过。
  • 向 Francois 反映:从 POD 类(或一般的聚合类)派生通常是一个坏主意和错误的设计。 std::array 是一个 POD 类。没有构造函数,没有赋值方法,没有虚函数,没有虚析构函数等。它可能是函数式的,但这是一个危险的,不推荐的设计。
  • @FERcsI,你能参考一些解释它有多危险的东西吗?
  • 如果在原来的基础上新建一个POD类应该是没有问题的。但是由于不推荐使用 POD 作为基类,理论上它可能会导致未定义的行为。但是,如果你打破这个并创建构造函数、析构函数、赋值运算符,编译器的行为可能与预期不同。

标签: c++ arrays c++11 operator-overloading


【解决方案1】:

如果要在赋值左侧使用operator[] 的返回值,则必须按引用返回数组元素,而不是按值返回。

您还有一个递归循环,因为您从自身内部调用自己的operator[]。您想改为调用基类的 operator[],因此您需要对其进行限定。

试试这个:

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

【讨论】:

    【解决方案2】:

    你可以使用:

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

    【讨论】:

    • 这确实需要解释一下。
    • 第三个看起来非常可选,除非它会绑定到 auto&& 之类的东西
    • 函数调用返回的@lili字面量数组,使用[]获取元素,按值返回是正确的。例如,处理引用生命周期延长。
    【解决方案3】:

    如果您希望对数组元素进行绑定检查访问,只需使用std::vector(或std::array)的at 方法,而不是[] 运算符。它就是为此目的而存在的,不要重新发明轮子:)。

    有关数组边界检查的文档,请参阅 reference。

    【讨论】:

      【解决方案4】:

      这是我对一维数组的解决方案:

      #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;
        }
      };
      

      这是对矩阵(多维)的扩展:

      #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;
        }
      };
      

      这是一个简单的测试应用程序:

      #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;
      }
      

      运行它,你会看到:

      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}}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-03
        • 2017-05-31
        • 2023-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-16
        相关资源
        最近更新 更多