It's a hell of a lot different from a vector<vector>. With a vector of vectors you have to do a lot more work to get the context right of it being a 2D array, for loops (either explicit or implicit) to size all the inner vectors and everything.
The code for a simple custom 2D array wrapper is just this:
template <class T> class grid
{
T *data;
public:
int sizex, sizey;
grid(int _sizex, int _sizey) : sizex(_sizex), sizey(_sizey)
{
data = new T[sizex * sizey];
}
~grid() { delete [] data; }
T &operator[](int x, int y) { return data[x + sizex * y]; }
}
// usage:
grid<char> charArray2D(20,20);
There you go, a fully encapsulated 2D array that can be sized at runtime, and passed as a single parameter, no bullshit or complexity. It's templatized, but the actual code is so minimal that it won't really bloat much if you make 2D arrays of different types. You can add optional bounds-checking inside the class, but have the advantage of being able to turn it off for higher speed, if you've sufficiently debugged the rest of your program.
with vector<vector> it's already instantiating two copies of the vector class and everything that goes with it, (vector<thing> and vector<vector<thing>>) including cloning the iterator types. Then, accessing the vector is a matter of chaining the calls through the methods of the two separate vector classes, so that's more runtime overhead.