91

C++0x 将使以下代码和类似代码格式不正确,因为它需要a到 a的所谓窄化转换doubleint

int a[] = { 1.0 };

我想知道这种初始化是否在现实世界的代码中被大量使用。此更改将破坏多少代码?如果您的代码完全受到影响,是否要在代码中解决这个问题?


参考见 n3225 的 8.5.4/6

缩小转换是隐式转换

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

8 回答 8

42

当我使用 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内部的转换{}

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

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

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

于 2011-01-14T12:25:07.177 回答
10

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

这些是否也在缩小转换范围?

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

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

于 2010-12-13T22:49:13.927 回答
8

我遇到的一个实际例子:

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

数字文字是隐含的double,它会导致提升。

于 2012-11-03T19:13:02.353 回答
7

如果有人被这样的事情抓住,我不会那么惊讶:

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?

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

于 2010-12-14T01:07:53.290 回答
6

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

CFLAGS += -std=c++0x -Wno-narrowing
于 2017-05-20T14:56:13.363 回答
4

缩小转换错误与隐式整数提升规则的交互非常糟糕。

我的代码错误,看起来像

struct char_t {
    char a;
}

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

这会产生一个缩小的转换错误(根据标准这是正确的)。原因是candd隐式地被提升为,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,则可以,并且在没有警告的情况下编译。我仍然认为这是 C++11 中的一个缺陷,但那里的人不这么认为——可能是因为不摆脱任何隐式整数转换(这是过去人们编写代码时遗留下来的问题)都不容易修复喜欢char a=b*c/d并期望它即使 (b*c) > CHAR_MAX) 或缩小转换错误(这可能是一件好事)也能正常工作。

于 2012-03-23T10:02:24.140 回答
3

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

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

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

于 2018-12-18T06:00:10.043 回答
1

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

于 2012-05-29T08:49:58.917 回答