[英]overload ambiguous (int -> int64_t vs int -> double)
為什么int
到int64_t
與int
到double
的隱式轉換不明確?
我會認為積分重載優先於浮點積分?
#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) {} ^
我的環境是:
-std=c++14
) int64_t
是我機器上的long int
:
/usr/include/stdint.h
:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
我已經在我的測試應用程序中使用靜態斷言確認了這一點:
static_assert(__WORDSIZE == 64, "");
static_assert(std::is_same<int64_t, long int>::value, "");
我的構建標志是:
-std=c++14 -Werror -Wall -Wextra -m64 -msse2 -msse4.2 -mfpmath=sse
-ftemplate-depth-128 -Wno-unused-parameter -pthread -g -ggdb3 -O0 -fno-inline
從 [over.ics.user] 表 12 我們有
如您所見,整數和浮點促銷具有相同的等級,而整數和浮點轉換具有相同的等級。
現在我們需要確定5 -> int64_t
是整數提升還是轉換。 如果我們檢查 [conv.prom]/1 我們發現
整數轉換等級 (4.13) 小於 int 等級的 bool、char16_t、char32_t 或 wchar_t 以外的整數類型的純右值可以轉換為 int 類型的純右值,如果 int 可以表示源類型的所有值; 否則,源純右值可以轉換為 unsigned int 類型的純右值。
提升在int
停止,所以我們必須查看 [conv.integral]/1 這是整數轉換,我們有
整數類型的純右值可以轉換為另一種整數類型的純右值。 無作用域枚舉類型的純右值可以轉換為整數類型的純右值。
這是怎么回事。 所以5 -> int64_t
是整數轉換而5 -> double
是浮點轉換,它們的排名相同,因此重載決議是不明確的。
恐怕這真的歸結為“因為它是”。
整數提升結束於int
; 沒有升級到大於int
類型。 所以,你只剩下兩個沼澤標准的隱式轉換,而且碰巧它們是同樣好的匹配。
[C++14: 4.5/1]:
除bool
、char16_t
、char32_t
或wchar_t
之外的整數類型的純右bool
,其整數轉換等級 (4.13) 小於int
的等級,可以轉換為int
類型的純右值如果int
可以表示源類型的所有值; 否則,源純右值可以轉換為unsigned int
類型的純右值。
[C++14: 4.5/7]:
這些轉換稱為積分提升。
[C++14: 5/10]:
[..]這種模式稱為通常的算術轉換,其定義如下:
- 如果任一操作數是作用域枚舉類型 (7.2),則不執行任何轉換; 如果另一個操作數不具有相同的類型,則表達式格式錯誤。
- 如果任一操作數的類型為
long double
,則另一個應轉換為long double
。- 否則,如果任一操作數為
double
,則另一個應轉換為double
。- 否則,如果任一操作數為
float
,則另一個應轉換為 float 。- 否則,應在兩個操作數上執行積分提升 (4.5) [..]
也許,當long int
和long long int
變得流行時(特別是通過cstdint
和朋友中的類型別名),標准可以被修改以向這些類型引入積分提升。 那么你的轉換就不會模棱兩可了。 但是,許多現有代碼也可能被破壞。
由於執行轉換是一種廉價的修復,我懷疑這是否值得標准委員會“修復”。
也許很明顯,可以以這種方式推廣相對較新的固定寬度char
類型:
[C++14: 4.5/2]:
char16_t
、char32_t
或wchar_t
(3.9.1) 類型的純右值可以轉換為以下第一個類型的純右值,可以表示其基礎類型的所有值:int
、unsigned int
、long int
、unsigned long int
、long long int
或unsigned long long int
。 如果該列表中的任何類型都不能表示其基礎類型的所有值,則可以將char16_t
、char32_t
或wchar_t
類型的純右值轉換為其基礎類型的純右值。
編譯器是對的...默認情況下,您的數字文字5
的類型為int
...
要進行該函數調用,必須發生重載決議,這基本上檢查了轉換的可行性。 請注意,這將是轉換而不是促銷的情況
現在,解釋為什么? 我會引用 cppreference 的故事:(我知道,不是標准的替代品)
在為重載決議選擇最佳可行函數時:
如果 F1 的所有參數的隱式轉換不比 F2 的所有參數的隱式轉換差,則確定 F1 是比 F2 更好的函數,並且
- F1 的至少一個參數的隱式轉換優於 F2 的該參數的相應隱式轉換
- 或者,...
- 或者,...
現在讓我們看看什么是隱式轉換序列:
隱式轉換序列包含以下內容,按此順序:
- 零個或一個標准轉換序列;
- . . . .
現在,讓我們看看標准的轉換序列是什么:
標准轉換序列按此順序包含以下內容:
- 零或一左值變換;
- 零個或一個數字提升或數字轉換;
- . . .
你有它,從這里,參考NathanOliver 的回答中的表格..
編譯器具有int
-> double
和int
-> int64_t
的內置隱式轉換。 它不知道,它需要使用哪一個。
如果問題發生在構造函數或類型轉換運算符中,您可以使用explicit
關鍵字來禁用歧義。 然后,您將需要為要調用它的類型編寫不同的重載方法(它們主要是進行顯式轉換 + 包裝器)。
在函數/方法的情況下,它要簡單得多:如果您有不需要隱式轉換的重載方法,編譯器會選擇它。 簡單開發
void foo(int64_t) {}
並解決了問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.