web-dev-qa-db-ja.com

C ++ 0xでの狭い変換。それは私だけですか、それとも壊滅的な変化のように聞こえますか?

C++ 0xは、次のコードと同様のコードを不正な形式にします。これは、いわゆる縮小変換doubleからintを必要とするためです。

int a[] = { 1.0 };

この種の初期化が実際のコードで多く使われているのではないかと思っています。この変更によりいくつのコードが破損しますか?コードがまったく影響を受けている場合、コードでこれを修正するのは大変ですか?


参考については、n3225の8.5.4/6を参照してください

縮小変換は暗黙的な変換です

  • 浮動小数点型から整数型へ、または
  • long doubleからdoubleまたはfloat、またはdoubleからfloat。ただし、ソースが定数式であり、変換後の実際の値が(正確に表現できない場合でも)表現できる値の範囲内にある場合、または
  • ソースが定数式であり、変換後の実際の値がターゲット型に適合し、元の型に戻されたときに元の値を生成する場合を除き、整数型または対象範囲外の列挙型から浮動小数点型へ、または
  • 整数型または対象範囲外の列挙型から、ソースが定数式であり、変換後の実際の値がターゲット型に適合し、元の値を生成する場合を除き、元の型のすべての値を表すことができない整数型元のタイプに変換されます。

GCCを使用したときに、この重大な変更に遭遇しました。コンパイラは、次のようなコードのエラーを出力しました。

_void foo(const unsigned long long &i)
{
    unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32};
}
_

関数void foo(const long long unsigned int&)内:

エラー:{}内の_(((long long unsigned int)i) & 4294967295ull)_の変換を_long long unsigned int_から_unsigned int_に絞り込む

エラー:{}内の_(((long long unsigned int)i) >> 32)_の変換を_long long unsigned int_から_unsigned int_に絞り込む

幸いなことに、エラーメッセージは簡単で、修正は簡単でした。

_void foo(const unsigned long long &i)
{
    unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF),
            static_cast<unsigned int>(i >> 32)};
}
_

コードは外部ライブラリにあり、1つのファイルに2つのオカレンスしかありませんでした。重大な変更が多くの​​コードに影響を与えるとは思わない。初心者は getconfused、 かもしれません。

41
Timothy003

過去12年間に書いたC++コードにこの種の問題があったことを知って、私は驚き、失望しました。しかし、ほとんどのコンパイラーは、私が何かを見逃していない限り、コンパイル時の「縮小」に関する警告をずっと吐き出していました。

これらもコンバージョンを絞り込んでいますか?

unsigned short b[] = { -1, INT_MAX };

もしそうなら、それらはあなたの浮動型から整数型の例よりも少し頻繁に現れるかもしれないと思います。

9
aschepler

誰かが次のようなものに巻き込まれても、私はそれほど驚かないでしょう:

float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};

(私の実装では、最後の2つはint/longに変換されたときに同じ結果を生成しないため、狭くなります)

ただし、これを書いたことは覚えていません。これは、限界の近似値が何かに役立つ場合にのみ役立ちます。

これは少なくとも漠然ともっともらしいようです:

void some_function(int val1, int val2) {
    float asfloat[] = {val1, val2};    // not in C++0x
    double asdouble[] = {val1, val2};  // not in C++0x
    int asint[] = {val1, val2};        // OK
    // now do something with the arrays
}

しかし、完全に説得力があるわけではありません。正確に2つの値があることがわかっているのに、なぜfloat floatval1 = val1, floatval1 = val2;ではなく配列に入れるのでしょうか。動機は何ですか、なぜコンパイルする必要がありますか(そして動作します、精度の損失がプログラムの許容精度内であれば)、float asfloat[] = {val1, val2};はそうではないのですか?いずれにせよ、2つのintから2つのfloatを初期化していますが、ある場合には、2つのfloatがたまたま集約のメンバーになっているだけです。

(特定の実装上で)ソース型のすべての値が宛先型で表現可能であり、元の値に変換できる場合でも、非定数式の結果が縮小変換になる場合は特に厳しいようです:

char i = something();
static_assert(CHAR_BIT == 8);
double ra[] = {i}; // how is this worse than using a constant value?

バグがないと仮定すると、おそらく修正は変換を明示的にすることです。マクロで奇妙なことをしているのでなければ、配列初期化子は配列の型の近く、または少なくともテンプレートパラメータに依存する可能性のある型を表すものの近くにしか表示されないと思います。したがって、キャストは、冗長であれば簡単にできます。

7
Steve Jessop

CFLAGSに-Wno-narrowingを追加してみてください。例えば:

CFLAGS += -std=c++0x -Wno-narrowing
5
Kukuh Indrayana

私が遭遇した実用的なインスタンス:

float x = 4.2; // an input argument
float a[2] = {x-0.5, x+0.5};

数値リテラルは暗黙的にdoubleであり、これが昇格を引き起こします。

5
Jed

縮小変換エラーは、暗黙的な整数プロモーションルールとの相互作用が不十分です。

次のようなコードでエラーが発生しました

struct char_t {
    char a;
}

void function(char c, char d) {
    char_t a = { c+d };
}

これにより、変換エラーが狭まります(標準に従って正しい)。理由は、cdが暗黙的にintに昇格され、結果のintを初期化子リストのcharに戻すことは許可されていないためです。 。

大藤

void function(char c, char d) {
    char a = c+d;
}

もちろん、まだ大丈夫です(そうでなければ、すべての地獄が解散します)。しかし、驚くべきことに、

template<char c, char d>
void function() {
    char_t a = { c+d };
}

cとdの合計がCHAR_MAXより小さい場合、警告なしでコンパイルできます。私はまだこれがC++ 11の欠陥だと思いますが、そこにいる人々はそうではないと思います-おそらく暗黙の整数変換(人々がコードを書いたときの過去からの遺物)を取り除くことなく修正するのは簡単ではないからです好む char a=b*c/dおよび(b * c)> CHAR_MAX)または変換エラーを狭める(おそらく良いことです)場合でも動作することを期待していました。

4
Gunther Piez

この機能の実際の経験から、C++ 03コードベースをC++ 11に移植することに伴う実際の苦労により、多くの場合gccがエラーからの警告に狭まったことが示されたため、これは実際に重大な変更でした。 gccバグレポートのこのコメント を参照してください。

この規格では、「準拠する実装は少なくとも1つの診断メッセージを発行する」ことのみを要求しているため、警告付きでプログラムをコンパイルすることは許可されています。アンドリューが言ったように、-Werror = narrowingを使用すると、必要に応じてエラーにすることができます。

G ++ 4.6ではエラーが発生しましたが、4.7では意図的に警告に変更されました。これは、多くの人々(私自身を含む)が、最も一般的な大きなC++ 03コードベースをC++ 11としてコンパイルしようとしています。 char c [] = {i、0};などの以前の整形式コード(iはcharの範囲内に収まる)エラーが発生し、char c [] = {(char)i、0}に変更する必要があった

1
Shafik Yaghmour

GCC-4.7では、変換を絞り込むためのエラーは発生しないように見えますが、代わりに警告が表示されます。

1
kyku