2017-01-09 55 views
4

比方說,我有功能重載所有標準整數類型:重載所有的基本整數類型足以捕獲所有整數?

void foo(char); 
void foo(signed char); 
void foo(short); 
void foo(int); 
void foo(long); 
void foo(long long); 
// .... all unsigned variants as well 

難道這些重載將無法找到類型,如int8_t或類似的東西適當超載?有沒有一種可移植的方式來處理這種過載?

引用如何?

爲了弄清楚這個問題:它來自這個問題的討論Why is int8_t read as a character?並聲稱可能有編譯器生成的整數類型不是基本C++類型的別名。所以在這種情況下,所有基本情況下的重載可能不會接受它。另一方面,我不能提供int8_t的過載,因爲在許多平臺上,它只是一個別名,並且會因重新定義現有過載而出錯。

+0

您可以將int8_t類型的函數定義爲刪除。 –

+1

而不是說「全部」,你可以產生一個明確的清單,你是什麼超載? (說「每一個的」簽名「和」未簽名「:將它減半是可以的)。因爲你所說的「全部」可能不是「全部」。 – Yakk

+1

取決於你對「接受」的定義。你可以將'int8_t'傳遞給一個定義爲接受'int'的函數,它會接受它。 – melpomene

回答

10

該標準不能保證展位ard整數類型是編譯器支持的唯一整數類型。事實上,C和C++標準明確授予權限的編譯器定義其他整數類型,統稱爲「擴展整型」:

也可能有實現定義擴展的有符號整數類型

同樣地,對於每一個擴展符號整數類型有時存在一個對應擴展的無符號整數類型 ...

擴展符號整數類型和擴展的無符號整數類型統稱爲擴展整型

而C標準並不禁止使用擴展整數類型實現stdint.h類型的實現。甚至有這種非規範符號,只是爲了說清楚:

其中一些類型可能表示實現定義的擴展整數類型。

如果你想要,可以採取任何整數類型的函數,你有兩種選擇:使它成爲一個模板(可能使用SFINAE不允許非整數類型),或者提供一個單一的重載需要std::intmax_t。該標準要求intmax_t是所支持的最大整數:

表示能夠表示 任何值的帶符號的整數類型的任何符號整型

當然「符號整型」,並且包括標準和擴展類型。因此,std::intmax_t可以處理該實現支持的任何有符號整數。

+0

「事實上,C和C++標準明確允許編譯器定義其他整數類型」你介意引用標準嗎? – Slava

+0

您提到了C標準,並沒有明確說出哪個標準是第一個報價。問題在於C++,而不是C。 (並且我沒有發現函數在C上重載) – Slava

+0

@Slava:C++的cstdint行爲完全參照C標準定義。所以如果你想知道這些類型的行爲,你*有*看看C標準。至於其他引用,其中大多數可能來自C或C++,因爲它們使用幾乎相同的語言。 –

1

int8_t將隱含地轉換爲int(事實上,當通過值取任何有符號整型將轉換爲intmax_t如果有一個函數採用此類型,並且通常整型類型之間隱式轉換(我已經調查過在那裏我兩個int和long類型提供過載奇怪的問題[他們不得不在那個特定的編譯器不同的大小]和C++編譯器抱怨過載決議將是模糊)

而且,通常int8_tsigned char是等價的。

+2

問題不是通常的問題,而是保證所有基本類型的重載將處理所有整數類型 – Slava

+0

一個複雜的整數(複數兩個分量都是整數),任何自定義類型都不是整數。只有int和char的所有變體,以及int8_t和uint32_t等typedef才被認爲是整型,並且它們都隱式地相互轉換(即使被迫是有損的,它仍然是隱含的) –

+2

看起來你不明白這個問題。我不是要求用戶定義的類型。問題是「如果編譯器允許創建不會被所有基本類型的重載處理的整數類型」 – Slava

0

這些重載可能不會接受像int8_t或類似的東西?

不,不適用於int8_t。即使int8_t不是標準整數類型之一的別名,它仍然可以轉換爲其他整數類型,並且促銷到int是明確優選的。因此,具有擴展int8_t的呼叫將解決重載之一。

但是,對於較大的類型,這仍然是一個問題。

+0

我不想刪除過載,問題是我無法以便攜的方式爲'int8_t'創建一個過載。 – Slava

+0

@Slava你當然可以創建一個過載。但是它也會成爲別名基本類型的超載。假設都不可移植:別名是標準類型,或別名是擴展類型。 – user2079303

+0

我不能在這種類型是別名的平臺上的基本類型上重載以外,也不能創建這樣的重載。 – Slava

1

不能保證每個平臺都會以同樣的方式定義固定寬度的整數類型,或者它們將根據任何給定平臺上的基本類型進行定義。因此,爲了確保您的重載將捕獲任何給定平臺上的固定寬度類型,您需要確定它們在該平臺上的實現方式。

雖然你可以用模板技巧做到這一點,但最簡單的方法是使用一個獨特的最小幫助程序,輸出每個固定寬度和基本類型的typeid,允許你比較它們的內部名稱以確定每個固定寬度類型如何在該編譯器的該平臺&上實現。

#include <cstdint> 
#include <iostream> 
#include <iomanip> 
#include <typeinfo> 

int main() { 
    std::cout << std::left; 
    std::cout << std::setw(10) << "int8_t: " << typeid(int8_t).name() << '\n' 
       << std::setw(10) << "int16_t: " << typeid(int16_t).name() << '\n' 
       << std::setw(10) << "int32_t: " << typeid(int32_t).name() << '\n' 
       << std::setw(10) << "int64_t: " << typeid(int64_t).name() << '\n' 
       << std::setw(10) << "uint8_t: " << typeid(uint8_t).name() << '\n' 
       << std::setw(10) << "uint16_t: " << typeid(uint16_t).name() << '\n' 
       << std::setw(10) << "uint32_t: " << typeid(uint32_t).name() << '\n' 
       << std::setw(10) << "uint64_t: " << typeid(uint64_t).name() << '\n' 
       << std::endl; 

    std::cout << std::setw(20) << "char: "    << typeid(char).name()    << '\n' 
       << std::setw(20) << "signed char: "  << typeid(signed char).name()  << '\n' 
       << std::setw(20) << "unsigned char: "  << typeid(unsigned char).name()  << '\n' 
       << std::setw(20) << "signed short: "  << typeid(signed short).name()  << '\n' 
       << std::setw(20) << "unsigned short: "  << typeid(unsigned short).name()  << '\n' 
       << std::setw(20) << "signed int: "   << typeid(signed int).name()   << '\n' 
       << std::setw(20) << "unsigned int: "  << typeid(unsigned int).name()  << '\n' 
       << std::setw(20) << "signed long: "  << typeid(signed long).name()  << '\n' 
       << std::setw(20) << "unsigned long: "  << typeid(unsigned long).name()  << '\n' 
       << std::setw(20) << "signed long long: " << typeid(signed long long).name() << '\n' 
       << std::setw(20) << "unsigned long long: " << typeid(unsigned long long).name() << '\n' 
       << std::endl; 
} 

輸出示例:

// MSVC 2015 (x86 & x64): 
int8_t: signed char 
int16_t: short 
int32_t: int 
int64_t: __int64 
uint8_t: unsigned char 
uint16_t: unsigned short 
uint32_t: unsigned int 
uint64_t: unsigned __int64 

char:    char 
signed char:  signed char 
unsigned char:  unsigned char 
signed short:  short 
unsigned short:  unsigned short 
signed int:   int 
unsigned int:  unsigned int 
signed long:  long 
unsigned long:  unsigned long 
signed long long: __int64 
unsigned long long: unsigned __int64 

// Clang & GCC (x64): 
int8_t: a 
int16_t: s 
int32_t: i 
int64_t: l 
uint8_t: h 
uint16_t: t 
uint32_t: j 
uint64_t: m 

char:    c 
signed char:  a 
unsigned char:  h 
signed short:  s 
unsigned short:  t 
signed int:   i 
unsigned int:  j 
signed long:  l 
unsigned long:  m 
signed long long: x 
unsigned long long: y 

從這:

  • MSVC(在x86 & x64的Windows中,因爲ILP32 & LLP64使用相同類型的大小):我們可以看到,固定寬度類型基於基本類型;注意,對於64位整數類型,編譯器將發出內部名稱__int64(來自之前long long是標準化的同義詞,如Bo Persson pointed out in the comments代替規範名稱long long
  • 鏘& GCC(在x64 * nix中):通過比較損壞的類型名稱,我們可以看到:
    • 8位類型基於char類型。
    • 16位類型基於short類型。
    • 32位類型基於int類型。
    • 64位類型基於long類型。

從這一點,我們知道,在這些平臺上,這些編譯器:

  • signed char來抓int8_t
  • unsigned char將捕獲uint8_t
  • signed short將捕獲int16_t
  • unsigned short將捕獲uint16_t
  • 等等...
  • [請注意,由於Windows的LLP64和* nix的LP64模式之間的差異,int64_t & uint64_t將由不同的重載捕獲。]

然後,您可以將此使用您可能希望使用的任何其他平臺和編譯器進行處理,從而允許您檢測何時需要進行特定於平臺的修改。


雖然這看起來像一個XY問題。你的主要問題是,你希望能夠重載固定寬度類型的平臺,他們不同於基本類型,但不知道如何做到這一點,而不會在平臺上破壞你的代碼。與基本類型相同。

要做到這一點,您需要找到一種方法來唯一標識您所在的平臺;最簡單的方法是looking for macros specific to that platform

void func(   char); 
void func( signed char); 
void func(unsigned char); 
void func(  short); 
void func(unsigned short); 
// ... 

#ifdef PLATFORM_WHERE_INT8_T_ISNT_SIGNED_CHAR 
    void func(int8_t); 
    void func(uint8_t); 
#endif 

#ifdef PLATFORM_WHERE_INT16_T_ISNT_SIGNED_SHORT 
    void func(int16_t); 
    void func(uint16_t); 
#endif 

// ... 

使用像這樣的設置,您可以定義附加重載,其中固定寬度的類型不是基本類型的平臺,而不會影響他們基本類型的平臺。這可以讓你做任何平臺特定的處理,然後把它們交給你的基本類型過載之一(如果有必要的話)。

+0

在MSVC中['__int64'只是'long long'的一個別名](https://msdn.microsoft.com/en-us/library/s3f49ktz.aspx)。它不是一個單獨的類型,只是在「long long」之前使用的舊名稱。 –

+0

@BoPersson好的,很高興知道。我會更新答案來提及這一點。 –