In general, you would just store an index, UID, handle, pointer or other 'name' in each tilemap cell, which refers to data that describes that tile. That's the easy part -- you have one unique thing (the tile) that reappears in many places, so you tag each place with a way to find the thing, then look it up; this pattern appears all over the place, not just in maps, not just in games.
A more interesting question is how you want to store the map cells. The easiest way is using a 2D or 3D array, dynamically allocated so that you can load maps of different sizes. This can be a bit wasteful of memory (e.g. if you have 'overhead' map layers with lots of empty space, you'll store a lot of 'empty' tiles in those cells, but this is not a problem on modern systems, though it can be a large annoyance if you have just one or few very tall layers (you spend an entire layer to represent just a few overhead tiles, multiplied by the number of layers needed). You can have a 2D array where each cell is a linked-list, which saves that unused memory, but can be more costly to iterate over. Or you can store in smaller chunks (say 8x8 tiles) and stream in only the chunks you need. Lots of options with different properties and tradeoffs.