I’m writing a program where I have created a std::vector of POD structs. One of the members of the struct is a unique identifier.
In order to be able to use std::binary_search I have to implement operator< for the struct. Following the guidelines here, I’m writing the full set of overloads for ==, !=, <, >, >= and <=.
This presents one issue I wasn’t sure how to handle. The vector will be ordered by the unique ID I assign each struct. Two structs are the same if they have the same identifier. However, it occurs to me that a situation could crop up if two structs have the same identifier but different data in the other members.
This should never happen. Would it be appropriate to have the comparison operator check the rest of the fields then and throw an exception if they’re different but the ID is the same? What sort of exception would be most appropriate?
This is merely an expansion on SCombinator’s answer.
The fact that you said “This should never happen.” means you want to use an assert, not an exception (or a combination of the two). An exception would hide the error – well, not hide, but you’ll be able to catch it and continue. It is better suited for exceptional situations which you didn’t really plan for – for example you’re attempting to open a file that doesn’t exist. It’s not really part of the logic that the file is missing, it’s your little brother accidentally deleting your file, or an aggressive anti-virus that thought it was a virus, or whatever – it’s just an exceptional situation.
If members with the same ID but otherwise different is something that should never happen, that’s basically an assertion. It’s part of the logic – part of the requirements if you will. Throwing an exception simply points this out, but there’s not really a way you can recover from it. By the time you realize something is wrong, it’s already 2 late. You have two objects with the same id, yet different, you don’t know which one is correct, you don’t know why the incorrect one exists, and so on. You probably don’t even want to recover from it. The application is already in a erroneous state – the two contradicting objects already exist. Your application is in an un-recovarable state – if you continue with it, you’ll probably get wrong results or worse.
If it’s not a critical assertion, you can also throw an exception afterwards, and provide a clean way to close the application, but that’s just a beautification.
In general, I follow a simple rule – If it’s something that can happen under extraordinary circumstances, I use exceptions. If it’s something that should never happen, and if it does, means something is seriously wrong with the logic in the code, I use an assert (and possibly a force crash).