缩小 C++0x 中的转换范围。是只有我这么认为,还是这听起来像是一个重大改变?

发布于 2024-10-07 14:05:45 字数 606 浏览 0 评论 0原文

C++0x 将使以下代码和类似代码格式错误,因为它需要从 doubleint 的所谓窄化转换

int a[] = { 1.0 };

我想知道这种初始化在现实世界的代码中是否经常使用。此更改会破坏多少代码?如果您的代码受到影响,需要付出很大的努力才能在代码中修复此问题吗?


有关参考,请参阅 n3225 的 8.5.4/6

缩小转换是隐式转换

  • 从浮点类型到整数类型,或者
  • 从long double到double或float,或者从double到float,除非源是常量表达式并且转换后的实际值在可以表示的值范围内(即使无法精确表示) ,或
  • 从整数类型或无作用域枚举类型到浮点类型,除非源是常量表达式,并且转换后的实际值将适合目标类型,并且在转换回原始值时将产生原始值输入,或
  • 从整数类型或无作用域枚举类型到无法表示原始类型的所有值的整数类型,除非源是常量表达式,并且转换后的实际值将适合目标类型并产生转换回原始类型时的原始值。

C++0x is going to make the following code and similar code ill-formed, because it requires a so-called narrowing conversion of a double to a int.

int a[] = { 1.0 };

I'm wondering whether this kind of initialization is used much in real world code. How many code will be broken by this change? Is it much effort to fix this in your code, if your code is affected at all?


For reference, see 8.5.4/6 of n3225

A narrowing conversion is an implicit conversion

  • from a floating-point type to an integer type, or
  • from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

不醒的梦 2024-10-14 14:05:45

当我使用 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 intunsigned int的缩小转换> 在 { } 内

错误:(((long long unsigned int)i) >> 32)long long unsigned intunsigned int<的缩小转换/code> 在 { }


幸运的是,错误消息很简单,修复也很简单:

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

代码位于外部库中,仅在一个文件。我认为重大更改不会影响太多代码。新手可能获取 但很困惑。

I ran into this breaking change when I used GCC. The compiler printed an error for code like this:

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

In function void foo(const long long unsigned int&):

error: narrowing conversion of (((long long unsigned int)i) & 4294967295ull) from long long unsigned int to unsigned int inside { }

error: narrowing conversion of (((long long unsigned int)i) >> 32) from long long unsigned int to unsigned int inside { }

Fortunately, the error messages were straightforward and the fix was simple:

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

The code was in an external library, with only two occurrences in one file. I don't think the breaking change will affect much code. Novices might get confused, though.

拍不死你 2024-10-14 14:05:45

如果我发现我在过去 12 年中编写的任何 C++ 代码都存在此类问题,我会对自己感到惊讶和失望。但大多数编译器都会一直发出有关任何编译时“缩小”的警告,除非我遗漏了一些东西。

这些是否也会缩小转化范围?

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

如果是这样,我认为它们可能比浮点类型到整数类型的示例更频繁地出现。

I would be surprised and disappointed in myself to learn that any of the C++ code I wrote in the last 12 years had this sort of problem. But most compilers would have spewed warnings about any compile-time "narrowings" all along, unless I'm missing something.

Are these also narrowing conversions?

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

If so, I think they might come up a bit more often than your floating-type to integral-type example.

生生漫 2024-10-14 14:05:45

尝试将 -Wno-narrowing 添加到您的 CFLAGS,例如:

CFLAGS += -std=c++0x -Wno-narrowing

Try adding -Wno-narrowing to your CFLAGS, for example :

CFLAGS += -std=c++0x -Wno-narrowing
葬心 2024-10-14 14:05:45

我遇到的一个实际例子:

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

数字文字隐式为 double ,这会导致提升。

A practical instance that I have encountered:

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

The numeric literal is implicitly double which causes promotion.

作妖 2024-10-14 14:05:45

如果有人遇到类似的情况,我不会感到惊讶:(

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

在我的实现中,最后两个在转换回 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
}

但它并不完全令人信服,因为如果我知道我恰好有两个值,为什么要把它们放在数组中而不是仅仅 float floatval1 = val1, floatval1 = val2; ?不过,动机是什么,为什么应该编译(并且可以工作,前提是精度损失在程序可接受的精度范围内),而 float asfloat[] = {val1, val2}; 不应该?无论哪种方式,我都会从两个整数初始化两个浮点数,只是在一种情况下,这两个浮点数恰好是聚合的成员。

在非常量表达式导致缩小转换的情况下,即使(在特定实现上)源类型的所有值都可以在目标类型中表示并且可以转换回其原始值,这似乎特别严酷:

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

假设没有错误,大概修复方法始终是使转换显式化。除非您对宏做了一些奇怪的事情,否则我认为数组初始值设定项仅出现在接近数组类型的地方,或者至少出现在表示类型的地方,这可能依赖于模板参数。因此,即使比较冗长,转换也应该很容易。

I wouldn't be all that surprised if somebody gets caught out by something like:

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

(on my implementation, the last two don't produce the same result when converted back to int/long, hence are narrowing)

I don't remember ever writing this, though. It's only useful if an approximation to the limits is useful for something.

This seems at least vaguely plausible too:

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
}

but it isn't entirely convincing, because if I know I have exactly two values, why put them in arrays rather than just float floatval1 = val1, floatval1 = val2;? What's the motivation, though, why that should compile (and work, provided the loss of precision is within acceptable accuracy for the program), while float asfloat[] = {val1, val2}; shouldn't? Either way I'm initializing two floats from two ints, it's just that in one case the two floats happen to be members of an aggregate.

That seems particularly harsh in cases where a non-constant expression results in a narrowing conversion even though (on a particular implementation), all values of the source type are representable in the destination type and convertible back to their original values:

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

Assuming there's no bug, presumably the fix is always to make the conversion explicit. Unless you're doing something odd with macros, I think an array initializer only appears close to the type of the array, or at least to something representing the type, which could be dependent on a template parameter. So a cast should be easy, if verbose.

离笑几人歌 2024-10-14 14:05:45

缩小转换错误与隐式整数提升规则相互作用很差。

我的代码有一个错误,看起来像

struct char_t {
    char a;
}

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

“产生缩小转换错误”(根据标准这是正确的)。原因是 cd 隐式提升为 int 并且生成的 int 不允许缩小范围回到初始化列表中的 char。

OTOH

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,则 Even 是可以的,并且编译时不会发出警告。我仍然认为这是 C++11 中的一个缺陷,但那里的人不这么认为 - 可能是因为如果不摆脱隐式整数转换(这是过去人们编写代码时的遗留物),就不容易修复就像 char a=b*c/d 一样,即使 (b*c) > CHAR_MAX) 也能正常工作,或者缩小转换错误(这可能是一件好事)。

Narrowing conversion errors interact badly with implicit integer promotion rules.

I had an error with code which looked like

struct char_t {
    char a;
}

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

Which produces an narrowing conversion error (which is correct according to the standard). The reason is that c and d implicitly get promoted to int and the resulting int isn't allowed to be narrowed back to char in an initializer list.

OTOH

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

is of course still fine (otherwise all hell would break loose). But surprisingly, even

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

is ok and compiles without a warning if the sum of c and d is less than CHAR_MAX. I still think this is a defect in C++11, but the people there think otherwise - possibly because it isn't easy to fix without get rid of either implicit integer conversion (which is a relict from the past, when people wrote code like char a=b*c/d and expected it to work even if (b*c) > CHAR_MAX) or narrowing conversion errors (which are possibly a good thing).

少女情怀诗 2024-10-14 14:05:45

这确实是一个重大变化,因为此功能的现实生活经验表明,由于将 C++03 代码库移植到 C++11 的现实生活中的痛苦,gcc 在许多情况下已将范围缩小为错误警告。请参阅gcc bug 报告中的此评论

该标准仅要求“合格的实现应发出至少一条诊断消息”,因此允许编译带有警告的程序。正如 Andrew 所说,-Werror=narrowing 允许您根据需要将其设置为错误。

G++ 4.6 给出了一个错误,但在 4.7 中有意将其更改为警告,因为许多人(包括我自己)发现缩小转换是在尝试执行操作时最常遇到的问题之一。将大型 C++03 代码库编译为 C++11。以前格式良好的代码,例如 char c[] = { i, 0 }; (其中 i 只会在 char 的范围内)导致错误,必须更改为 char c[] = { (char)i, 0 }

It was indeed a breaking change as real life experience with this feature has shown gcc had turned narrowing into a warning from an error for many cases due to real life pains with porting C++03 code bases to C++11. See this comment in a gcc bug report:

The standard only requires that "a conforming implementation shall issue at least one diagnostic message" so compiling the program with a warning is allowed. As Andrew said, -Werror=narrowing allows you to make it an error if you want.

G++ 4.6 gave an error but it was changed to a warning intentionally for 4.7 because many people (myself included) found that narrowing conversions where one of the most commonly encountered problems when trying to compile large C++03 codebases as C++11. Previously well-formed code such as char c[] = { i, 0 }; (where i will only ever be within the range of char) caused errors and had to be changed to char c[] = { (char)i, 0 }

鲸落 2024-10-14 14:05:45

看起来 GCC-4.7 不再给出缩小转换的错误,而是发出警告。

It looks like GCC-4.7 no longer gives errors for narrowing conversions, but warnings instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文