2016-11-21 9 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より短い整数型に変更されたとき

警告::|((unsigned char型「(int型)(((符号なし 文字)(​​(int型)x1.X ::値))の変換を狭め、その後原因私は警告を受ける整数プロモーションへstd::uint8_t INT「 から 'X' に ')((INT)x2.X ::値)))::内部U {別名unsigned char型は} {} [-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)}; 
} 

質問:整数プロモーションとナルロ意図した結果(*)で翼キャスト混乱?特にこれらはキャストであるため、符号の前後を変更する(unsigned char - >int->unsigned char)。 Uが署名されている場合、つまりstd::int8_t(これは自分のコードでは署名されませんが、そのような場合は動作が不明です)。

私の常識は、コードは完全にOKだと言いますが、私のC++のパラノイアは、少なくとも実装定義の動作の可能性があると言います。

(*)の場合であることははっきりしていない(または私はめちゃめちゃに)意図した動作は(Bをセット/クリアオペアンプで、x2がマスクされ、x1が値である)ビットを設定またはクリアすることです

答えて

2

符号なしの型を使用すると、すべてがOKになります。

4.7積分変換[conv.integral]
...
2宛先タイプが符号なしの場合、結果の値が最小である:狭くが完全に定義されている符号なし目標整数型のため、標準的な義務ソースに一致する符号なし整数 整数(モジュロ2n、nは符号なしタイプを表すために使用されるビット数)

しかし、ターゲット・タイプが署名されている場合、結果は、実装は、次の段落(鉱山を強調する)あたり、定義される:宛先タイプが署名されている場合ならば、値は変更されない

3宛先タイプで表すことができます。 の場合、値は実装定義です。

一般的な実装では、コンパイラは符号なしまたは符号付きのどちらかの低レベルのバイトを保持するだけで簡単に変換を絞り込むことができます。しかし、この標準では、実装がの場合にはと定義されています。実装では、元の値をターゲットタイプで表現できない場合に符号付きタイプに値を絞り込むと、0となり、依然として適合していることを文書化できます。ところで


、C++、Cはしばしばプロセスが同じようにコンバージョンとして、最後のケースは、信号を上げる可能性があるため、Cの標準は、わずかに異なることに留意すべきである。

6.3.1.3 [変換]符号付きおよび符号なし整数
...
3そうでない場合、新しい型は符号付きであり、その値を表すことはできません。 の結果が実装定義のであるか、または実装定義の信号がになっています。さらに

CとC++は異なる言語である確認...

+0

(署名された対象):このビット操作の場合には、私は、任意の結果は、このように、バック先の型に表現することができると思うだろう実装定義の振る舞いを避ける。私は正しいですか? – bolov

+0

すべての* normal *アーキテクチャであなたは正しいです。 (int8_tは2の補数ですが、普通はintではありません)、それが標準で許されるのかどうかは分かりません。今のところ、私はそれに決定的な答えはありません... –

+1

@bolov:私は標準を掘り下げ、 'int8_t'型をサポートするインプリメンテーションを禁じますが、* signとmagnitude *を使って単純なintを持っていません。その場合、 '((int8_t)-128)| (int8_t)1'は '-129'を与えることができます。積分プロモーション(intへの変換)が適用されます。したがって、符号付きの型のビット演算が安定している(オペランドと同じ型になります)ことは何も保証されていません。 –