RFC: Lightweight C++ 2D array template with runtime dinemsions Any comments, critiques or questions about this? I'm using this as the base for a few rectangular board based toy programs like Conway's life and snake. Usage includes configuration of board size at runtime, hence the type doesn't carry size info, only the instances.

#include &lt;utility&gt;

template&lt;typename type&gt;
class Array2D {
   int y;
   type *data;
public:
   // We can't do a copy since we don't know
   // what size to allocate the array.
   Array2D(const Array2D&amp;) = delete;
   Array2D&amp; operator=(const Array2D&amp;) = delete;

   Array2D(Array2D&amp;&amp; move) noexcept :
      y(std::exchange(move.y, 0)),
      data(std::exchange(move.data, nullptr)) {}

   Array2D&amp; operator=(Array2D&amp;&amp; move) noexcept {
      std::swap(y, move.y);
      std::swap(data, move.data);
   }

   Array2D( int _x, int _y ) : y(_y) {
      data = new type[_x*y];
   }

   type const *operator[](int i) const {
      return data + (i * y);
   }

   type *operator[](int i) {
      return data + (i * y);
   }

   ~Array2D() {
      delete[] data;
   }
};
Overall
It's fine as far as it goes.
I don't see anything wrong with the implementation.
Note: Since you use none checked version of operator[] you should have the ability to validate that your inputs are correct. So your array needs the ability to return the dimensions of the array in both the x and y dimensions so the code can validate the inputs when required.
Next Step
Currently this is not a container.
There are a few more requirements to make this a standards compliant container. If you want to go this direction.
see: https://en.cppreference.com/w/cpp/named_req/Container
Some improvements:
User defined Types (and by extension template types) should always have an initial capitol letter. This is for humans so we can easily distiguish types from objects:
template&lt;typename type&gt;

I don't like this as now type looks like an object to me when I read it in the code.
Put the destructor next to the constructors.
When code reviewing I want to make sure that your destructor releases the resources correctly as I read. Not scroll back when I find the destructor at the bottom. Also they logically group together.
Why is none copy?
I don't see the logic in that.
Add a swap method (noexcept) and swap free standing function. That comes in handy when interacting with the standard library functions.
It would be nice to simply loop over an array:
Array2D&lt;int&gt;   data = get2DArray();

for(auto const&amp; val: data) {
    std::cout &lt;&lt; val &lt;&lt; &quot; &quot;;
}

The ability to extract rows to loop over would also be nice (this is an alternative to the above).
for(auto const&amp; row: data) {
    for(auto const&amp; val: row) {
        std::cout &lt;&lt; val &lt;&lt; &quot; &quot;; 
    }
    std::cout &lt;&lt; &quot;\n&quot;;
}

You have the none checked operator[] implemented fine. But sometimes it is nice to have checked versions so add the method at() that does the same thing but validates its input first.
This is very C like type const *object The * placed next to the object is a standard C convention but in C++ the convention is the opposite and (by tradition) you put the * next to the type rather than the objet (as it is part of the type information).