
That's no different from saying that a program that uses 2 + 3i as if it were a real number has undefined behavior.
I'm not sure that this is the same thing. What you have here is a potential a type error. The previous question was whether NaN is a well-formed value. I should amend that statement: NaN is not a well-formed value when interpreted as a real number. It is a perfectly reasonable state for a floating point object. I had previously omitted that last part ("when interpreted as a real number"), and I shouldn't have. It's actually very important to understanding when the use NaN can lead to undefined behavior and when it cannot. And why.
If you make an assumption about the value of an object, then it's likely to be undefined behavior if that assumption doesn't hold.
That is certainly true.
Some algorithms obviously won't work, but others like copy that don't depend on comparison will work fine. You can even use find or is_sorted if you pass a custom predicate that handles NaN's in a way consistent with what the algorithm expects. In short, the fact that NaN's cause undefined behavior is a property of the concept map of comparison for double, not a property of double in itself.
Algorithms where NaN is interpreted as a real number, whether through equality, ordering, or numeric operations, result in undefined behavior in the sense that the postconditions of the algorithm are broken. The previous examples of find() and sort() fall into that category. Copy does not require NaN to be interpreted as a real number, so a program copy()ing sequences of doubles containing NaNs will not result in undefined behavior. Custom predicates change the interpretation the floating point state. The find_if() algorithm does not impose any interpretation on the iterator's value type; the predicate does. Passing isnan() as a predicate does not interpret the value type as a real number, just a bit pattern. A lambda doing this: [](double n) { return n < 0.0; } does. It may result in undefined behavior. Similarly, a custom relation can impose alternative rules for NaN, making it act like optional<double>. But this is not an interpretation of NaN as a real number. It is an interpretation that extends real numbers with an optional null, missing, or maybe state. This has nothing to do with concept maps. That was a language feature. This is far more fundamental.
As far as I'm concerned, any value that can exist at all is a member of the set of values represented by the type.
The meaning of "value" is not arbitrary. The C++ community seems to have converged on the idea the set of states we associate with a type's "value" as those elements which can be distinguished by equality comparison. That set of states tends to conform to some abstraction: naturals, integers, reals, iterators (or positions), sequences, and sets. The notion of "value" shouldn't simply be taken as the set of valid states encoded by a type.
The fact that some values are forbidden in certain contexts is irrelevant.
I am not disagreeing that programs written using NaN are necessarily wrong, but I do disagree that this is irrelevant. Understanding how a type's states are interpreted, in different contexts, is important to understanding the behavior of our programs and if (or more likely when) we invoke undefined behavior by breaking assumptions. Andrew