Warning C4267 in the expression unsigned n = str.find(substr)
When porting 32-bit code on a 64-bit system, Visual C++ compiler may generate a lot of warnings C4267 for the code where the result of the function std::string::find() is saved into a variable of the unsigned type. Here is such an example:
using namespace std; string s("123456789"); unsigned n = s.find("a"); if (n == string::npos) cout << "OK" << endl; else cout << "64-bit error" << endl;
The function find() returns the value of the type string::size_type that in practice is analogous to the type size_t. In a 32-bit program the types string::size_type and unsigned coincide and have the size 32 bits.
When compiling the code example given above in the 64-bit mode, the compiler generates the following warning:
warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
The reason for that is that the size of the type string::size_type extends to 64 bits in a 64-bit program. Accordingly, the compiler warns you about a loss of significant bits when the 64-bit type is implicitly converted to the 32-bit type.
When studying this case, programmers often make this logical mistake:
My program never has and will never have strings longer than some Kbytes and Gbytes all the more. So, the unsigned type is quite enough to keep the position of the substring found.
The result of such a supposition is that programmers suppress the compiler warning with an explicit type conversion. It allows them to correct only one fragment of the code without involving the following ones. Below is the code "corrected" in this way:
using namespace std; string s("123456789"); unsigned n = (unsigned)s.find("a"); if (n == string::npos) cout << "OK" << endl; else cout << "64-bit error" << endl;
This way of correction is bad. The code contains an error and the warning that could help detect it is now suppressed with the explicit type conversion. If you launch this code in the 64-bit mode, it will print the message "64-bit error" instead of "OK".
The error occurs because the function find() returns the value string::npos that equals 0xFFFFFFFFFFFFFFFFui64. This value is cut to the size 0xFFFFFFFFu and saved into a 32-bit variable. As a result, the condition 0xFFFFFFFFu == 0xFFFFFFFFFFFFFFFFui64 is always false.
To correctly fix such warnings you should use correct types instead of suppressing them with explicit type conversions. In our case we should use a variable of the type string::size_type to store the result. Here is an example of a proper correction of the code:
using namespace std; string s("123456789"); string::size_type n = s.find("a"); if (n == string::npos) cout << "OK" << endl; else cout << "64-bit error" << endl;
Of course, string::size_type makes the code a bit complicated and less readable, so you can compromise between full accuracy and simplicity of the code using the type size_t. But this is up to you what to choose.
The compiler warning C4267 is useful because it allows you to detect various 64-bit errors. Unfortunately, sometimes this warning may be suppressed by an explicit type conversion that was written when developing the 32-bit code. In this case you may use the static analyzer Viva64 included into PVS-Studio to diagnose the related issues. The analyzer generates the diagnostic warnings V201 and V202 that allow you to detect unsafe explicit type conversions when developing 64-bit applications.