Performance et. al. are not arguments in this matter.
dynamic_cast is thee mechanism designed into C++ to handle the exact scenario the OP is asking about.
If you don't want to use RTTI then you can't use RTTI and down-casting properly requires RTTI.
If you turn it off, you are just going to have to implement it yourself with a virtual call to get an enumeration (and design some mechanism to ensure those ID's are unique).
Next is if you use a interface-based design then your RTTI implementation is going to have to handle reporting multiple IDs ...
COM's QueryInterfacePointer is the abomination that resulted from going down this path.
If you have a tree or whatever of ITiles and want to know which of them implement ITilePlayer then you check for that when you put the instance into the container (with dynamic_cast) and maintain a second parallel container with just ITilePlayer's in it. Now you can iterate the player tiles without testing for it during an iteration of the general tiles thus avoiding dynamic cast during the part that matters. (Since it's a mix of different types of tiles, static_cast is not appropriate.)
I personally wouldn't consider the player a tile - I would have an 'overlay' concept the movable entities / sprites implemented.
Render in two (or more) passes. Render the tiles then render the entities (overlayed on the tiles).
You now have a game-engine reason to main separate list of the different things as well.
Though it does beg the question why not just have two functions to add stuff to the game and treat the tiles and entities differently (thus avoiding RTTI & dynamic cast entirely).