Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to use negative integers with size_t?

I just saw some C++ code like this. It was using a condition to decide whether to walk forward or backward through a std::vector. The compiler doesn't complain, but I thought size_t was unsigned. Is this dangerous?

vector<int> v { 1,2,3,4,5 };    
bool rev = true;

size_t start, end, di;
if (rev) {
    start = v.size()-1;
    end = -1;
    di = -1;
}
else {
    start = 0;
    end = v.size();
    di = 1;
}

for (auto i=start; i!=end; i+=di) {
    cout << v[i] << endl;
}
like image 619
Rob N Avatar asked Jan 31 '15 03:01

Rob N


2 Answers

It's well defined to use unsigned integers (and size_t is unsigned) this way, with wraparound: that behavior is guaranteed by the standard, as opposed to with signed integers, where it's not guaranteed by the standard.

It is however needlessly clever.

As a general rule, to avoid problems due to implicit wrapping promotions to unsigned, use unsigned integers for bit-level stuff, use signed integers for numbers. Where you need a signed integer corresponding to size_t there's ptrdiff_t for you. Define an n_items function with signed result, e.g.

using Size = ptrdiff_t;

template< class Container >
auto n_items( Container const& c )
    -> Size
{ return end( c ) - begin( c ); }

and you're set to go, no more sillywarnings from the compiler.


Instead of the too clever given code

vector<int> v { 1,2,3,4,5 };    
bool rev = true;

size_t start, end, di;
if (rev) {
    start = v.size()-1;
    end = -1;
    di = -1;
}
else {
    start = 0;
    end = v.size();
    di = 1;
}

for (auto i=start; i!=end; i+=di) {
    cout << v[i] << endl;

do e.g.

const vector<int> v { 1,2,3,4,5 };    
const bool reverse = true;  // whatever

for( int i = 0; i < n_items( v );  ++i )
{
    const int j = (reverse? n_items( v ) - i - 1 : i);
    cout << v[j] << endl;
}
like image 65
Cheers and hth. - Alf Avatar answered Oct 21 '22 17:10

Cheers and hth. - Alf


Whenever I need to deal with signed types, I always use:

typedef std::make_signed<std::size_t>::type ssize_t; // Since C++11

...as a signed alternative to std::size_t.

I appreciate this question is a few years old, but I'm hoping that will help others. Credit to moodycamel::ConcurrentQueue.

like image 38
ZeroDefect Avatar answered Oct 21 '22 17:10

ZeroDefect