When compiling C++ with overloaded function definitions why is the promotion behaviour different between signed and unsigned integer types? Is this expected behaviour?
In the example below, the call to "fail" in main is ambiguous but the call to "pass" is not.
unsigned int fail(unsigned int a) {
return a;
}
unsigned int fail(unsigned short a) {
return a;
}
int pass(int a) {
return a;
}
int pass(short a) {
return a;
}
int main(){
unsigned char a;
char b;
fail(a);
pass(b);
return 0;
}
Sample output (from clang, VS compiler gives something similar):
fail.cpp:22:3: error: call to 'fail' is ambiguous
fail(a);
^~~~
fail.cpp:1:14: note: candidate function
unsigned int fail(unsigned int a) {
^
fail.cpp:5:14: note: candidate function
unsigned int fail(unsigned short a) {
^
1 error generated.
According to integral promotion (emphasis mine):
The following implicit conversions are classified as integral promotions:
signed char [...] can be converted to int;
unsigned char [...] can be converted to int if it can hold its entire value range, and unsigned int otherwise;
char can be converted to int or unsigned int depending on the underlying type: signed char or unsigned char (see above);
Note that all other conversions are not promotions; for example, overload resolution chooses char -> int (promotion) over char -> short (conversion).
In your case, given that int is able to hold the entire value range of both signed char and unsigned char, only int pass(int a) is a promotion which is more preferable than the remaining three which are conversions, and there is no preference among the conversions.
Those implicit conversions indeed follow different rules.
char -> int is a promotion
char -> short is a conversion
and promotion is chosen over conversion because it forbids any precision loss. That's why pass passes.
Whereas
unsigned char -> unsigned int is a promotion only if int can't represent the full range of values unsigned chars can; if not, the promotion is unsigned char -> int.
I suspect that, in your case, int can indeed represent all values in the range of unsigned char. This means that fail has a choice between two two-steps conversion paths: unsigned char -> int -> unsigned int and unsigned char -> int -> unsigned short and can't decide between them.
source: http://en.cppreference.com/w/cpp/language/implicit_conversion
Related
#include <iostream>
#define TRY_INT
void testRun()
{
#ifdef TRY_INT //test with unsigned
unsigned int value1{1}; //define some unsigned variables
unsigned int value2{1};
unsigned int value3{2};
#else //test with fixed width
uint16_t value1{1}; //define fixed width unsigned variables
uint16_t value2{1};
uint16_t value3{2};
#endif
if ( value1 > value2 - value3 )
{
std::cout << value1 << " is bigger than: " << value2 - value3 << "\n";
}
else
{
std::cout << value1 << " is smaller than: " << value2 - value3 << "\n";
}
}
int main()
{
testRun();
return 0;
}
with unsigned integers I get:
1 is smaller than: 4294967295
with fixed width unsigned int, output is:
1 is smaller than: -1
My expectation was it would wrap around as well, does this have something to do with std::cout?
I guess it is caused by integral promotion. Citing form cppreference:
...arithmetic operators do not accept types smaller than int as arguments, and integral promotions are automatically applied after lvalue-to-rvalue conversion, if applicable.
unsigned char, char8_t (since C++20) or unsigned short can be converted to int if it can hold its entire value range...
Consequently, if uint16_t is just an alias for unsigned short on your implementation, value2 - value3 is calculated with int type and the result is also int, that's why -1 is shown.
With unsigned int, no promotion is applied and the whole calculation is performed in this type.
In the latest online C++ Draft, see [conv.prom/1]:
A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.
unsigned int is equivalent to uint32_t and unsigned short int is equivalent to uint16_t.
Therefore, if you use unsigned short int instead of unsigned int you will get the same behavior as for uint16_t.
Why do you get -1?
Integral promotion will try to convert unsigned short int to int if int can hold all possible values of unsigned short int. On the other hand, if that is not the case, integral promotion to unsigned int will be performed.
Therefore the subtraction is most likely done in the type int, not uint16_t.
When I run
#include <stdio.h>
#include <typeinfo.h>
#include <type_traits>
struct S { operator unsigned long long() const { return 3ULL << 30; } };
int main()
{
printf("%s, 0x%llX, %d\n",
typeid(true ? S() : 0).name(),
(unsigned long long)(1 ? S() : 0),
std::is_same<
std::common_type<int, unsigned long long>::type,
unsigned long long
>::value);
}
I expect to get back unsigned long long, 0xC0000000, 1.
However, I get back int, 0xFFFFFFFFC0000000, 1. Why?
The way I see it, the 0 should be converted to unsigned long long, because the conversion ranks are as follows:
rank(int) < rank(long) < rank(long long) = rank(unsigned long long)
Yet I see a direct conversion of unsigned long long to int, which I'm struggling to justify...
The compiler sees S and int, not unsigned long long and int.
It doesn't go through all possible pairs of conversions to pick the best one when a class type is involved, because it can't do that in general, because a conversion operator might be templated, and the rules should not change depending on whether that is the case or not.
All it can do is tell if one type is convertible to the other, and in this case, that means it detects that S is convertible to int, so it performs that conversion without integer promotion ranks entering the picture at all.
how this convertion done from unsigned char* to unsigned short?
#include<iostream>
using namespace std;
int main()
{
unsigned short x;
x= (unsigned char*)&x - (unsigned char*) 0x8888;
cout<<us<<endl;
return 0;
}
when I am trying to add like this
x= (unsigned char*)&x + (unsigned char*) 0x8888;
Then error comes up. it says "invalid operands of types 'unsigned char*' and 'unsigned char*' to binary 'operator+' " please help me to understand this syntax.
Subtraction operation can be performed on two address data type but not addition.
For addition, either both operand of + shall be of arithmetic type or one operand shall be a pointer and other shall be an integer type.
C++11-§ 5.7/1:
For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type.
You could convert pointer to int/short/long using cstdint C++11 as below, But I'm not certain whether it is advisable to add pointers as mentioned by #haccks
#include<iostream>
#include <cstdint>
using namespace std;
int main()
{
unsigned short x;
//x= (unsigned char*)&x + (unsigned char*) 0x8888;
x = static_cast<unsigned short>(reinterpret_cast<uintptr_t>(&x)) + static_cast<unsigned short>(0x8888);
cout<<x<<endl;
return 0;
}
Refer to this link for more information.
When unsigned/signed long int a; is possible
why unsigned/signed long float/double a; is not possible ?
Why do I get too many types in declaration error for the latter and not for the former ?
There are three floating point types: float, double and long double. None of these have unsigned equivalents, so putting signed or unsigned in front of them is not valid. There is no such type as long float.
You are getting that message because a long double exists, but an unsigned long double does not. unsigned can also be interpreted as an int, therefore you possess two types in the latter declaration: unsigned and long double. I do not believe there is a long float in C++.
That is because the first (long int) is a documented variable type, while the second isn't.
The data types that the C++ language supports are:
char
unsigned char
signed char
int
unsigned int
signed int
short int
unsigned short int
signed short int
long int
signed long int
unsigned long int
float
double
long double
error: overflow in implicit constant conversion [-Werror=overflow]
#include<stdio.h>
int main()
{
char ch=200;
printf("\n%d",ch);
return 0;
}
I am running this code on http://ideone.com/YNkKT6#view_edit_box and getting the implicit conversion error.
What modification do I need and what is the reason?
n3376 3.9.1/1
Plain char, signed char, and unsigned char are three distinct types. A char, a signed char, and an
unsigned char occupy the same amount of storage and have the same alignment requirements (3.11); that is,
they have the same object representation.
What is char is implementation-defined, so, you need unsigned char here, that handles values (0-255).
looks like your char is signed char, which accept value from -128 to 127. and 200 is too large for it and will overflow to be a negative number.
to fix it, change char to int or unsigned char
int main()
{
unsigned char ch=200;
printf("\n%d",ch);
return 0;
}