TxAG98
TxAG98

Reputation: 1120

C++ type conversion precedence in a return

I have a code that is being changed to compile in a 64-bit mode where previously it was being compiled in Win32 for various reasons. This has caused some cleanup work to address some warnings, so I'm picking through code and found something that looks like this:

class foo 
{

public:
    int foo() { return data_.size()-1; }

private:
    std::vector<int> data_;
};

The size() method on STL containers returns unsigned values. The return value is being cast into a signed integer value so there's a conversion that will occur at some point.

I'm not sure about the precedence here though. Will the value that size() is returning get cast to an int and then have 1 subtracted, which would result in the return value being -1 if size was zero? Or will we subtract 1 from an unsigned int, possibly doing bad things if the container is empty when this gets called?

Thanks!

Upvotes: 1

Views: 158

Answers (5)

Rakib
Rakib

Reputation: 7625

Your second guess is correct. In general, evaluating any expression of the form

exp1 op exp2

works in the steps

  1. evaluate exp1
  2. evaluate exp2
  3. apply op on the values of exp1 and exp2

Note: steps 1 and 2 can occur in any sequence. The point is each operand is evaluated before applying the operator.

So in this case exp1 will evaluated as an unsigned int value first, and may have undesirable effect.

Upvotes: 0

user2249683
user2249683

Reputation:

It will be unsigned = unsigned - 1; return signed(unsigned) where unsigned(0) - 1 == unsigned(max)

From 4.7 Integral conversions

A prvalue of an integer type can be converted to a prvalue of another integer type. A prvalue of an unscoped enumeration type can be converted to a prvalue of an integer type. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note ] If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

Hence any unsigned value greater than the maximum signed value leads to implementation defined behavior.

Upvotes: 3

Tobias
Tobias

Reputation: 5198

The next program returns i=-1. The bit pattern of std::numeric_limits<unsigned>::max() is that one of the int -1. This is the reason why your code works in w32.

#include <iostream>
#include <limits>

int main() {
  std::cout << "i=" << static_cast<int>(std::numeric_limits<unsigned>::max()) << '\n';
}

/*
  Local Variables:
  compile-command: "g++ test.cc -o a.exe && ./a.exe"
  End:
*/

But, you rely in your code on the translation of signed integral types and unsigned ones. The types must fit! Therefore, better use std::ptrdiff_t and std::size_t.

#include <iostream>
#include <limits>

int main() {
  std::cout << "i=" << static_cast<std::ptrdiff_t>(std::numeric_limits<std::size_t>::max()) << '\n';
}

/*
  Local Variables:
  compile-command: "g++ test.cc -o a.exe && ./a.exe"
  End:
*/

Upvotes: 0

ggia
ggia

Reputation: 58

If the size is 0 the result is not -1 but a very large integer "18446744073709551615" (unsigned(max)).

#include <vector>
#include <iostream>

int main()
{ 
    std::vector<int> nums {};

    std::cout << "nums contains " << nums.size()-1 << " elements.\n";
    // nums contains 18446744073709551615 elements.
}

http://coliru.stacked-crooked.com/a/a69d4af99ba77f47

Upvotes: 1

R Sahu
R Sahu

Reputation: 206697

When data_.size()-1 is evaluated as an unsigned integer. When data_.size() is 0, the function could return a very large positive number instead of -1.

Your best option:

int foo() { int s = data_.size(); return s-1; }

Upvotes: 0

Related Questions