2016-04-29 80 views
4

下面的代碼編譯沒有警告:位移導致奇怪類型轉換

std::uint16_t a = 12; 
std::uint16_t b = a & 0x003f; 

然而,隨着按位沿執行比特移位並導致「隱式轉換警告」:

std::uint16_t b = (a & 0x003f) << 10; // Warning generated. 

gcc和clang都抱怨說有一個從intuint16_t的隱式轉換,但我不明白爲什麼引入位移會導致右手錶達突然評估爲int

編輯:對於鏗鏘聲,我編譯了-std=c++14 -Weverything標誌;對於gcc,我編譯了-std=c++14 -Wall -Wconversion標誌。

+0

在這兩種情況下,表達式的計算結果都是「int」。你會提供編譯器設置(可能是一個在線的例子)這個警告?我沒有在一個快速實驗中得到這個警告,既沒有來自海灣合作委員會,也沒有從鐺 – AnT

+0

@AnT嘗試'-Wconversion'。那麼你應該看到警告。 – Andrew

+1

_「如果傳遞給算術運算符的操作數是整數或非範圍枚舉類型,那麼在執行任何其他操作之前(但在左值到右值轉換之後(如果適用)),操作數會進行整數提升。」_ [http:// en.cppreference.com/w/cpp/language/operator_arithmetic](http://en.cppreference.com/w/cpp/language/operator_arithmetic)另請參見[this](http://stackoverflow.com/questions/) 36925291 /比特移位左和 - 丟棄比特?noredirect = 1個#comment61414105_36925291)。 – ZDF

回答

1

但我不明白爲什麼引入位移將導致右手錶達突然評估爲int。

我想你誤解了警告。在這兩種情況下,表達式評估爲int,但在第一種情況下,結果總是適合於uint16_t,在第二種情況下不適用。看起來編譯器足夠聰明,可以檢測到它並僅在第二種情況下生成警告。

+0

@Ant,因爲它仍然適合'uint16_t',你可以嘗試二進制'或'而不是? – Slava

+0

@Slava:是的,明白了。不知何故,我忘了更換'&'。 – AnT

+1

'unsigned char a = 1; unsigned char b =(a&0x01)<< 4;'會產生相同的警告。 'unsigned char b =(1&0x01)<< 4;'會落入「足夠聰明」的位置。 – ZDF

2

使用整數類型進行任何算術運算始終至少提前(有時,但在本例中,不使用gcc,unsignedint。正如您在this example中所看到的那樣,這也適用於您的第一個,無警告的變體。

解決這些(不可否​​認的是)令人驚訝的整數升級規則可能是最好的方法是從開始使用unsigned int(或在普通平臺上使用uint32_t)。

如果您不能或不想使用更大的類型,你可以static_cast整個表達式的回std::uint16_t結果:

std::uint16_t b = static_cast<std::uint16_t>((a & 0x003f) << 10); 

這將正確導致RHS-值模2^16。

1

cppreference.com「如果傳遞給算術運算的操作數是整數或無作用域的枚舉類型,那麼任何其他行動之前(但左值到右值的轉換,如適用)後,操作數進行積分的推廣 「。

例如:

byte a = 1; 
byte b = a << byte(1); 
  1. a1被晉升爲intint(a)int(byte(1))
  2. a向左移動一個位置:int result = int(a) << int(byte(1))(結果是int)。
  3. result存儲在b中。由於intbyte寬,所以會發出警告。

如果操作數常量表達式,編譯器可能能夠在編譯時計算的結果,並出具當且僅當結果的警告,不適合在目的地:

byte b = 1 << 1; // no warning: does not exceed 8 bits 
byte b = 1 << 8; // warning: exceeds 8 bits 

或者,使用constexpr

constexpr byte a = 1; 
byte b = a << 1; // no warning: it fits in 8 bits 
byte b = a << 8; // warning: it does not fit in 8 bits