簡體   English   中英

重載歧義(int -> int64_t vs int -> double)

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

為什么intint64_tintdouble的隱式轉換不明確?

我會認為積分重載優先於浮點積分?

#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) {} ^

我的環境是:

  • x86_64
  • g++-5.4(使用-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]:boolchar16_tchar32_twchar_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 intlong long int變得流行時(特別是通過cstdint和朋友中的類型別名),標准可以被修改以向這些類型引入積分提升。 那么你的轉換就不會模棱兩可了。 但是,許多現有代碼也可能被破壞。

由於執行轉換是一種廉價的修復,我懷疑這是否值得標准委員會“修復”。

也許很明顯,可以以這種方式推廣相對較新的固定寬度char類型:

[C++14: 4.5/2]: char16_tchar32_twchar_t (3.9.1) 類型的純右值可以轉換為以下第一個類型的純右值,可以表示其基礎類型的所有值: intunsigned intlong intunsigned long intlong long intunsigned long long int 如果該列表中的任何類型都不能表示其基礎類型的所有值,則可以將char16_tchar32_twchar_t類型的純右值轉換為其基礎類型的純右值。

編譯器是對的...默認情況下,您的數字文字5的類型為int ...

要進行該函數調用,必須發生重載決議,這基本上檢查了轉換的可行性。 請注意,這將是轉換而不是促銷的情況

現在,解釋為什么? 我會引用 cppreference 的故事:(我知道,不是標准的替代品)

在為重載決議選擇最佳可行函數時:

如果 F1 的所有參數的隱式轉換不比 F2 的所有參數的隱式轉換差,則確定 F1 是比 F2 更好的函數,並且

  1. F1 的至少一個參數的隱式轉換優於 F2 的該參數的相應隱式轉換
  2. 或者,...
  3. 或者,...

現在讓我們看看什么是隱式轉換序列

隱式轉換序列包含以下內容,按此順序:

  1. 零個或一個標准轉換序列
  2. . . . .

現在,讓我們看看標准的轉換序列是什么:

標准轉換序列按此順序包含以下內容:

  1. 零或一左值變換;
  2. 零個或一個數字提升或數字轉換
  3. . . .

你有它,從這里,參考NathanOliver 的回答中的表格..

編譯器具有int -> doubleint -> int64_t的內置隱式轉換。 它不知道,它需要使用哪一個。

如果問題發生在構造函數或類型轉換運算符中,您可以使用explicit關鍵字來禁用歧義。 然后,您將需要為要調用它的類型編寫不同的重載方法(它們主要是進行顯式轉換 + 包裝器)。

在函數/方法的情況下,它要簡單得多:如果您有不需要隱式轉換的重載方法,編譯器會選擇它。 簡單開發

void foo(int64_t) {}

並解決了問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM