Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

If you're replacing types in this manner you don't scrutinize the usages. You design (or choose) the new type such that it actually is compatible with the old type. For example std::vector and std::list can be replaced with each other, because they either have the same functions with analogous behavior, or you get compiler errors if you use any of the non-overlapping functions. What you can't do is replace an std::vector with a class whose clear() fills the container with default-constructed instances of the objects.

In other words, you concentrate your review on the original and new type, instead of the usages.



> You design (or choose) the new type such that it actually is compatible with the old type.

Yes I agree, in that case sure, but when refactoring the case can arise that a new type must no longer compatible with the old type, then auto becomes a hindrance.

std::vector and std::list have different behaviour regarding the validity of iterators after deletion (EDIT: and insertion so it seems!), to pick an example.

Maybe I'm just paranoid?


>the case can arise that a new type must no longer compatible with the old type

Then you make sure that whatever causes an incompatibility also causes a compilation error. You can't rely on text searches and IDEs for something like that.

>std::vector and std::list have different behaviour regarding the validity of iterators after deletion

Fair enough, perhaps not the best-chosen example. I was thinking about them purely as collections, rather than as part of resource management. Checking their behavior with automated tools becomes much more difficult once people start taking pointers into elements. But then again, that's true of any class. If, for example, a member function returns a reference to a member and someone gets its address, now that location is implicitly relying on the internal stability of the class in a way that's invisible to the type system.


I think the example is perfectly apt, and I would not know where to start wrapping a std::list/std::vector implementation to pick up on runtime iterator invalidation.


Since I was trying to give an example of two compatible classes, no, it's not apt, since their member functions have incompatible side effects.

Hm... Hypothetically, with a lot of effort you could design a dummy class (A) that implements only the members you want to investigate and where necessary returns a different dummy class (B) representing the element type. If someone ever tries to take the address of a B (you have to delete operator&() and/or get() if it's some kind of smart pointer) then you know you might be dealing with iterator invalidation.


Ah, well I guess I did take it as an example of something that was incompatible. It served well enough as a vehicle to drive the conversation forwards.

The iterator invalidation occurs when push_back(), insert() or erase() are called, presumably among others, so you'd also want to overload the iterator increment and decrement operators too (oh!, not to forget end(), or rend() if you are going the other way...). I'm not sure what operators and methods would be called on passing to an std::algorithm like std::find or std::sort. Most likely the only way to find out for certain would be to make everything inaccessible and replace piecemeal until the compiler was happy to run to completion.

I'd want to take a closer look where it's instantiated, but if all the uses are 'auto', well let's just say I'd be unhappy to say the least.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: