Code:
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
using std::cerr;
using std::cout;
using std::stringstream;
using std::string;
using std::for_each;
void convert(const string& a_value)
{
unsigned short i;
if (stringstream(a_value) >> i)
cout << a_value << " converted to " << i << ".\n";
else
cerr << a_value << " failed to convert.\n";
}
int main()
{
string inputs[] = { "abc", "10", "999999999999999999999", "-10", "0" };
for_each(inputs, inputs + (sizeof(inputs)/sizeof(inputs[0])), convert);
return 0;
}
Output from Visual Studio Compiler (v7, v8, v9, v10):
abc failed to convert. 10 converted to 10. 999999999999999999999 failed to convert. -10 converted to 65526. 0 converted to 0.
Output from g++ (v4.1.2, v4.3.4):
abc failed to convert. 10 converted to 10. 999999999999999999999 failed to convert. -10 failed to convert. 0 converted to 0.
I expected the "-10" to fail to be converted to an unsigned short but it succeeds with the VC compilers. Is this a:
- bug in the VC compilers ?
- bug in the GNU compilers and I have an incorrect expectation ?
- an implementation defined behaviour ?
The answer depends on what version of C++ you are using. C++03 and
earlier required the input to conform to what
sscanfdoes (using herethe
"%hi"input specifier), andsscanfreads an integral value intoa (signed) short, with no overflow detection; the results are then
assigned (with implicit conversion) to your
unsigned short. C++11requires the equivalent of calling
strtoull, which doesn’t allow the-sign, and requires an error in case of overflow (which is undefinedbehavior in
sscanf, and thus C++03).In practice, all reasonable implementations of C++03 did check for
overlow, and the “undefined behavior” in such cases corresponded to that
which is now required. On the other hand, they were required to
accept the minus sign, which is now (logically) forbidden.
EDIT (correction):
On rereading the requirements of
strtoull, I find that it does require accepting the minus sign. So as stupid as it seems, the standard does require input to an unsigned integral type to accept the minus sign. (Note too that the behavior ofstrtoulldepends on the global C locale, which may accept additional posibilities.)EDIT (further clarification):
As ectamur points out, this should be an error (in C++11), because
(unsigned long long)( -10 )will be too large to be represented in anunsigned short. On the other hand, it is still undefined behavior in pre-C++03 (which is perhaps what VC++ is conforming to—so whatever they do is “correct”).