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;
}
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;
}
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.
I can't speak to how safe that code is but I think it's a pretty poor style. A better way would be to use iterators which support forward or reverse iteration.
For example:
std::vector<int> v = { 1, 2, 3, 4, 5 };
bool rev = true;
if (rev)
{
for (auto itr = v.rbegin(); itr != v.rend(); ++itr)
{
std::cout << *itr << "\n";
}
}
else
{
for (auto itr = v.begin(); itr != v.end(); ++itr)
{
std::cout << *itr << "\n";
}
}
Is it safe to use negative integers with size_t?
No, it is dangerous. Overflow.
size_t a = -1;
std::cout << a << "\n";
Output:
4294967295 // depends on the system, largest value possible here