C++0x 中的缩小转换。是我一个人,还是这听起来像是一个重大变化?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4434140/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:24:24  来源:igfitidea点击:

Narrowing conversions in C++0x. Is it just me, or does this sound like a breaking change?

c++c++11surveyaggregate-initialization

提问by Johannes Schaub - litb

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

C++0x 将使下面的代码和类似的代码格式错误,因为它需要一个所谓的 a到 a 的缩小转换doubleint

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

参考n3225的8.5.4/6

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 ?oating-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.

收缩转换是隐式转换

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

采纳答案by Timothy003

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

我在使用 GCC 时遇到了这个重大变化。编译器为这样的代码打印了一个错误:

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 intto unsigned intinside { }

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

在功能上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内部 {} 的转换

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 getconfused,though.

该代码位于外部库中,在一个文件中仅出现两次。我认为重大更改不会影响很多代码。不过,新手可能感到困惑

回答by aschepler

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.

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

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.

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

回答by Steve Jessop

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)

(在我的实现中,最后两个在转换回 int/long 时不会产生相同的结果,因此正在缩小)

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.

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

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.

假设没有错误,大概修复总是使转换显式。除非你对宏做了一些奇怪的事情,否则我认为数组初始值设定项只会出现在接近数组的类型,或者至少接近于表示类型的东西,这可能取决于模板参数。因此,如果冗长,演员表应该很容易。

回答by Jed

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 doublewhich causes promotion.

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

回答by Kukuh Indrayana

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

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

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

回答by Gunther Piez

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 cand dimplicitly get promoted to intand the resulting intisn't allowed to be narrowed back to char in an initializer list.

这会产生缩小转换错误(根据标准是正确的)。原因是candd隐式地被提升为int并且结果int不允许在初始化列表中缩小回 char 。

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/dand expected it to work even if (b*c) > CHAR_MAX) or narrowing conversion errors (which are possibly a good thing).

如果 c 和 d 的总和小于 CHAR_MAX,则可以并且在没有警告的情况下编译。我仍然认为这是 C++11 中的一个缺陷,但那里的人不这么认为 - 可能是因为如果不摆脱任何隐式整数转换(这是过去人们编写代码时的遗留问题)就不容易修复喜欢char a=b*c/d并期待它的工作即使(b * C)> CHAR_MAX)或缩小转换错误(这可能是一件好事)。

回答by Shafik Yaghmour

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:

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

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.7because 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 }

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

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

回答by kyku

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

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