简体   繁体   English

重载歧义(int -> int64_t vs int -> double)

[英]overload ambiguous (int -> int64_t vs int -> double)

Why is the implicit conversion of int to int64_t vs int to double ambiguous?为什么intint64_tintdouble的隐式转换不明确?

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 x86_64
  • g++-5.4 (with -std=c++14 ) g++-5.4(使用-std=c++14

int64_t is a long int on my machine: int64_t是我机器上的long int

/usr/include/stdint.h : /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从 [over.ics.user] 表 12 我们有

在此处输入图片说明

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.现在我们需要确定5 -> int64_t是整数提升还是转换。 If we check [conv.prom]/1 we find如果我们检查 [conv.prom]/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;整数转换等级 (4.13) 小于 int 等级的 bool、char16_t、char32_t 或 wchar_t 以外的整数类型的纯右值可以转换为 int 类型的纯右值,如果 int 可以表示源类型的所有值; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.否则,源纯右值可以转换为 unsigned int 类型的纯右值。

The promotion stops at int so we have to look at [conv.integral]/1 which is integer conversion and we have提升在int停止,所以我们必须查看 [conv.integral]/1 这是整数转换,我们有

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.所以5 -> int64_t是整数转换而5 -> double是浮点转换,它们的排名相同,因此重载决议是不明确的。

I'm afraid this really comes down to " because it is ".恐怕这真的归结为“因为它是”。

Integer promotion ends at int ;整数提升结束于int there is no promotion to types larger than int .没有升级到大于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; [C++14: 4.5/1]:boolchar16_tchar32_twchar_t之外的整数类型的纯右bool ,其整数转换等级 (4.13) 小于int的等级,可以转换为int类型的纯右值如果int可以表示源类型的所有值; otherwise, the source prvalue can be converted to a prvalue of type unsigned int .否则,源纯右值可以转换为unsigned int类型的纯右值。

[C++14: 4.5/7]: These conversions are called integral promotions . [C++14: 4.5/7]:这些转换称为积分提升

[C++14: 5/10]: [..] This pattern is called the usual arithmetic conversions , which are defined as follows: [C++14: 5/10]: [..]这种模式称为通常的算术转换,其定义如下:

  • If either operand is of scoped enumeration type (7.2), no conversions are performed;如果任一操作数是作用域枚举类型 (7.2),则不执行任何转换; 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 .如果任一操作数的类型为long double ,则另一个应转换为long double
  • Otherwise, if either operand is double , the other shall be converted to double .否则,如果任一操作数为double ,则另一个应转换为double
  • Otherwise, if either operand is float , the other shall be converted to float.否则,如果任一操作数为float ,则另一个应转换为 float 。
  • Otherwise, the integral promotions (4.5) shall be performed on both operands [..]否则,应在两个操作数上执行积分提升 (4.5) [..]

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.也许,当long intlong long int变得流行时(特别是通过cstdint和朋友中的类型别名),标准可以被修改以向这些类型引入积分提升。 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:也许很明显,可以以这种方式推广相对较新的固定宽度char类型:

[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 . [C++14: 4.5/2]: char16_tchar32_twchar_t (3.9.1) 类型的纯右值可以转换为以下第一个类型的纯右值,可以表示其基础类型的所有值: intunsigned intlong intunsigned long intlong long intunsigned 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.如果该列表中的任何类型都不能表示其基础类型的所有值,则可以将char16_tchar32_twchar_t类型的纯右值转换为其基础类型的纯右值。

The compiler is right... By default, your numeric literal 5 is of type int ...编译器是对的...默认情况下,您的数字文字5的类型为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)我会引用 cppreference 的故事:(我知道,不是标准的替代品)

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如果 F1 的所有参数的隐式转换不比 F2 的所有参数的隐式转换差,则确定 F1 是比 F2 更好的函数,并且

  1. there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2 F1 的至少一个参数的隐式转换优于 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 ..你有它,从这里,参考NathanOliver 的回答中的表格..

The compiler has the builtin implicit conversions for both int -> double and for int -> int64_t .编译器具有int -> doubleint -> 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.如果问题发生在构造函数或类型转换运算符中,您可以使用explicit关键字来禁用歧义。 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.并解决了问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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