There is a time and a place for it, and it's sometimes useful when doing some heavy Generic Programming. Like, in theory if you had to design your own tuple type (I know std already has one, but ignoring that), the function to access a tuple's value might be an auto because it's difficult to even type out the template instantiation necessary for the return type, much less come up with it. Another place I use it: In my graphics library you can define pixels with an arbitrary memory footprint. Different amounts of bits for different channels, like RGB565 or YUV888 etc. Because of the arbitrary nature of it the integer values for each channel may be a different type. For example, while a channel probably won't be more than a uint8_t can hold (8-bits) it might be (12 bits? uint16_t would be necessary) Because of that, when I go to assign values from arbitrary pixel formats I don't actually *know* what type it is, other than some kind of integer of 64 bits or less (based on static_assert constraints). So I could always promote it to a uint64_t but that creates other problems when you have to cast down again. So auto is what's for dinner.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix