问题
In a Going Native 2013 talk, the panel had suggested avoiding unsigned integer types
when specifying variables that "can't be negative".
12:15: "Use signed integers unless you need 2's compliment arithmetic or a bit pattern."
12:55: "Use ints until you have a reason not to. Don't use unsigned unless you are fiddling with bit patterns, and never mix signed and unsigned."
42:45: "Whenever you mix signed and unsigned numbers you get trouble. The rules are just very surprising, and they turn up in code in the strangest places. They correlate very strongly with bugs. When people use unsigned integer numbers, they usually have a reason. The reason will be something like "well, it can't be negative" ... When you think you can't have negative numbers, you will have someone who initializes your unsigned with -2, and think they get -2. It is just highly error prone. ... There are far too many integer types. There are far too lenient rules for mixing them together, and it's a major bug source. Which is why I'm saying, stay as simple as you can. Use integers until you really really need something else."
This is understood, but there is no mention to alternatives when designing interfaces that must only accept positive numbers. I could document it:
//NOTE: i must be positive!
void function(int i);
I could rely on debug assertions everywhere:
void function(int i){
assert(i >= 0);
}
What I'd really like, is to specify it at the type level. This is a stronger contract. I want to make sure the function is incapable of accepting an integer that is negative. This is where I would normally choose an unsigned, but given that this is a discouraged practice, what are my alternatives?
Can a type like this be created to satisfy the value constraint?
void function(positive<int> i);
Does it make sense to do this?
回答1:
The reason why having the parameter unsigned solves nothing is because passing a run-time negative value into such a function checks nothing. -1 will be reinterpreted as 4294967295 and the program will silently continue.
Only if you try to pass a compile-time known constant a warning will be raised.
If you want to check every parameter you pass into your function, even at run-time, having an assert is the simplest way.
If you want to be more fancy and descriptive, you can define your own type positive which would:
- allow silent casts to
int(demotion) - allow casts from
int, but performing anassertwhen doing so (promotion) - support arithmetic operations
This will definitely make your code ``cleaner'' with a clear intent - but that is much more coding.
回答2:
But it is technically impossible to prevent negative values from being passed because only by running the code can you enumerate through the function's argument range.
Run-time assertions should be fine as you can remove them from a release build. This is normal practice, even in complicated codes for example from CGAL:
template<class Inter_pt_coplanar,class Point_3,class Halfedge_handle>
std::pair<Inter_pt_coplanar,Inter_pt_coplanar>
decision_tree(const Point_3* a,const Point_3* b,const Point_3* c,
const Point_3* p,const Point_3* q,
const Orientation& pqa,const Orientation& pqb,const Orientation& pqc,
Halfedge_handle pq,
Halfedge_handle ca,Halfedge_handle ab,Halfedge_handle bc)
{
CGAL_precondition(pqa!=NEGATIVE);
CGAL_precondition(pqb!=NEGATIVE);
CGAL_precondition(pqc!=POSITIVE);
http://doc.cgal.org/latest/Manual/devman_checks.html
来源:https://stackoverflow.com/questions/37717573/how-do-i-specify-a-non-negative-number-at-the-type-level-if-i-shouldnt-be-usin