2016-11-21 81 views
2

tl; dr位操作是否安全,並且在整數提升時的預期行爲(類型短於int)?使用整數提升的位操作

例如

uint8_t a, b, c; 
a = b & ~c; 

這是什麼,我有一個粗略的MCVE:

struct X { // this is actually templated 
    using U = unsigned; // U is actually a dependent name and can change 
    U value; 
}; 

template <bool B> auto foo(X x1, X x2) -> X 
{ 
    if (B) 
    return {x1.value | x2.value}; 
    else 
    return {x1.value & ~x2.value}; 
} 

這個偉大的工程,但是當U變更爲整型短於int,例如std::uint8_t則由於整型的提升,我得到一個警告:

警告:縮小的「(INT)轉換(((無符號 字符)((int)的x1.X ::值))|((無符號字符)((int)的x2.X ::值)))」從 'INT' 爲 'X ::∪{又名無符號字符}' 內{} [-Wnarrowing]

所以我添加一個static_cast

struct X { 
    using U = std::uint8_t; 
    U value; 
}; 

template <bool B> auto foo(X x1, X x2) -> X 
{ 
    if (B) 
    return {static_cast<X::U>(x1.value | x2.value)}; 
    else 
    return {static_cast<X::U>(x1.value & ~x2.value)}; 
} 

問題:能否整數提升,然後narro翼投與預期結果(*)混亂?特別是因爲這些是演員改變簽名的前後(unsigned char - >int - >unsigned char)。如果U已簽名,那麼怎麼辦?std::int8_t(它不會在我的代碼中籤名,但會對此行爲感到好奇)。

我常見的sens說代碼完全正常,但是我的C++偏執狂說至少有一個實現定義行爲的機會。

(*)是它的不明確的情況下(或I弄亂)的預期行爲是設置或清除位(x1是值,x2是掩模,B是設置/清除操作)

回答

2

如果您使用無符號類型,則全部都可以。

4.7積分轉化[conv.integral]
...
2如果目標類型爲無符號的,所得到的值是最小的。對於無符號整數目標類型,變窄是完全定義的標準任務無符號整數與源一致 integer(模2n,其中n是用於表示無符號類型的位數)。

但是,如果目標類型有符號,結果實現定義,每下一段落(強調雷):

3如果目的地是帶符號的數值不變,如果它可以用目標類型表示; 否則,值爲實現定義的

在常見的實現一切都會好的,因爲它是唯一保持低水平字節無符號或符號類型編譯器簡單根本就縮小轉換。但該標準只要求執行定義會發生什麼。當原始值不能用目標類型表示時,實現可以記錄將值縮小爲帶符號類型的值給出0,並且仍然是符合的。


順便說一句,作爲C++和C經常工藝轉換以同樣的方式,但應注意的是,C標準稍有不同,因爲最後一種情況下可以提高信號:

6.3.1.3 [轉換]有符號和無符號整數
...
3否則,新類型有符號且值不能在其中表示; 結果是實現定義的或者實現定義的信號被提升

仍然是一個確認,C和C++是不同的語言......

+0

(簽訂目標):這些位操作的情況下,我認爲任何結果可以表示回目標類型,從而避免實現定義的行爲。我對麼? – bolov

+0

在所有*正常*架構中,你是對的。我想知道什麼可能會破壞那個('int8_t'是兩個補碼,但是普通的'int'不是),並且如果這可以按照標準被允許的話。現在我沒有明確的答案... –

+1

@bolov:我已經深入瞭解標準,並且沒有任何東西禁止一個實現支持'int8_t'類型,但是使用*符號和幅度*來獲得普通的'int'。在這種情況下'((int8_t)-128)| (int8_t)1'可以給'-129'應用積分促銷(轉換爲int)。因此,沒有什麼能保證按位操作對簽名類型是穩定的(導致與操作數類型相同)。 –