For one thing, duck typing is always vulnerable to code that is syntactically correct but semantically nonsense. For example,
employee.fire()
and nuclear_missile.fire()
have quite different meanings, but if all you write is auto x = get_an_x()
x.fire()
then nothing in the language or compiler will stop you or notice the changed implication in this scenario.C++ is also vulnerable to a slightly more sinister variation because many conversions can be made implicitly, and sometimes those conversions are lossy. For example, suppose x was converted from an integral value to a floating point one because get_an_x needed was upgraded to offer more precision. Unfortunately, all the code you've got that later compares x to a known value using == might now be broken (comparing floating point values without a suitable tolerance) and again depending on how you've got your compiler's warnings set up you might never notice.
If C++ had a stronger type system, then at least the latter issue would be less dangerous, but with the language we're talking about today I think over-use of auto is a risk that shouldn't be ignored in code reviews.
It depends on how you code, I suppose. I use var very frequently in C# now after being averse to it at first. I've never run into an issue where var caused problems in my code (rarely my IDE won't be able to pick up on what type it should be, but it compiles fine).
If you're changing a variable between compatible types, like a parent class, or changing to a different type of enumerable class where usage is exactly the same, you don't really run into issues. If you're changing the entire usage of a class (like from a vector to an array, or something even more different) than you probably should not simply re-declare the variable, you should be rewriting that code.
You wouldn't change
vector<int> numbers = new vector<int>;
to int* numbers = new int[512];
Without making accommodating changes to the following code, would you? Then you shouldn't change auto numbers = new vector<int>;
to auto numbers = new int[512];
Without making changes to the rest of the code either. It's exactly the same as before, except you get to type less and it clutters the screen less when you don't need to write types multiple times on the same line. Especially for very long types names.