简体   繁体   中英

overload ambiguous (int -> int64_t vs int -> double)

Why is the implicit conversion of int to int64_t vs int to double ambiguous?

I would have thought the integral overload would have precedence over integral to floating point?

#include <stdint.h>

void foo(double) {}
void foo(int64_t) {}

int main()
{
    foo(5);
    return 0;
}
 main.cpp: In function 'int main()': main.cpp:8:10: error: call of overloaded 'foo(int)' is ambiguous foo(5); ^ main.cpp:3:6: note: candidate: void foo(double) void foo(double) {} ^ main.cpp:4:6: note: candidate: void foo(int64_t) void foo(int64_t) {} ^

My environment is:

  • x86_64
  • g++-5.4 (with -std=c++14 )

int64_t is a long int on my machine:

/usr/include/stdint.h :

 # if __WORDSIZE == 64
 typedef long int        int64_t;
 # else

I have confirmed this with a static assert in my test app:

static_assert(__WORDSIZE == 64, "");
static_assert(std::is_same<int64_t, long int>::value, "");

My build flags are:

-std=c++14 -Werror -Wall -Wextra -m64 -msse2 -msse4.2 -mfpmath=sse 
-ftemplate-depth-128 -Wno-unused-parameter -pthread  -g -ggdb3 -O0 -fno-inline

From [over.ics.user] table 12 we have

在此处输入图片说明

As you can see integer and floating point promotions have the same rank and integer and floating point conversions have the same rank.

Now we need to determine if 5 -> int64_t is a integer promotion or conversion. If we check [conv.prom]/1 we find

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) 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.

The promotion stops at int so we have to look at [conv.integral]/1 which is integer conversion and we have

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.

Which is what is going on. So 5 -> int64_t is integer conversion and 5 -> double is floating point conversion which are both ranked the same so the overload resolution is ambiguous.

I'm afraid this really comes down to " because it is ".

Integer promotion ends at int ; there is no promotion to types larger than int . So, you're left with two bog-standard implicit conversions, and it just so happens that they are equally good matches.

[C++14: 4.5/1]: A prvalue of an integer type other than bool , char16_t , char32_t , or wchar_t whose integer conversion rank (4.13) 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 .

[C++14: 4.5/7]: These conversions are called integral promotions .

[C++14: 5/10]: [..] This pattern is called the usual arithmetic conversions , which are defined as follows:

  • If either operand is of scoped enumeration type (7.2), no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.
  • If either operand is of type long double , the other shall be converted to long double .
  • Otherwise, if either operand is double , the other shall be converted to double .
  • Otherwise, if either operand is float , the other shall be converted to float.
  • Otherwise, the integral promotions (4.5) shall be performed on both operands [..]

Perhaps, when the use of long int and long long int became popular (particular through type aliases in cstdint and friends), the standard could have been modified to introduce integral promotions to these types. Then your conversion would not be ambiguous. However, lots of existing code could also have been broken.

Since performing a cast instead is a cheap fix, I doubt this is deemed worthwhile to "fix" by the standard committee.

Perhaps tellingly, the relatively new fixed-width char types can be promoted in this fashion:

[C++14: 4.5/2]: A prvalue of type char16_t , char32_t , or wchar_t (3.9.1) can be converted to a prvalue of the first of the following types that can represent all the values of its underlying type: int , unsigned int , long int , unsigned long int , long long int , or unsigned long long int . If none of the types in that list can represent all the values of its underlying type, a prvalue of type char16_t , char32_t , or wchar_t can be converted to a prvalue of its underlying type.

The compiler is right... By default, your numeric literal 5 is of type int ...

To make that function call, overload resolution must happen, which basically checks the viability of conversion. Note, it's going to be a case of conversion not promotion

Now, to explain why? I'll quote the story from cppreference: (I know, not a replacement for the standard)

In the selection of best viable function for overload resolution :

F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and

  1. there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
  2. or, ...
  3. or, ...

Now let us see what implicit conversion sequence is:

Implicit conversion sequence consists of the following, in this order:

  1. zero or one standard conversion sequence ;
  2. . . . .

Now, let us see what a standard conversion sequence is:

A standard conversion sequence consists of the following, in this order:

  1. zero or one lvalue transformation;
  2. zero or one numeric promotion or numeric conversion ;
  3. . . .

There you have it, from here, refer to the table in NathanOliver's answer ..

The compiler has the builtin implicit conversions for both int -> double and for int -> int64_t . It does not know, which one does it need to use.

If the problem had happened in a constructor or type conversion operator, you could have used the explicit keyword to disable ambiguities. Then you will need to write different overloaded methods for the types you want to call it (they are mostly doing an explicit conversion + wrapper).

In the case of functions / methods, it is much simpler: if you have the overloaded method which does not require an implicit conversion, the compiler will choose that. Simply develop

void foo(int64_t) {}

and problem solved.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM