So why is it c.size() in the worst case? Note that erase has a return value:

Return value 1-2) Iterator following the last removed element. [...]

The function has to find the "next element". std::unordered_set stores its data in so called bucket lists. Ideally, this is the next available slot in the same bucket list as the one which accommodates the element which you erase. Worst case, it is the last available slot in some other bucket (and hence it scales with the size of the container). This depends on the insert/erase history of the container. You can have a look at the libcxx implementation here, there is a loop traversing the nodes in the bucket list (the mechanism is well explained by @eeroika's answer).

Besides, not that (also from the docs on erase):

References and iterators to the erased elements are invalidated

So dereferencing the iterator it after you erased it from the set is undefined behavior. You can fix it by