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


85

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

int a[] = { 1.0 };

我想知道在现实世界的代码中是否经常使用这种初始化。此更改将破坏多少代码?如果您的代码受到影响,是否需要花费很多精力在代码中进行修复?


供参考,请参阅n3225的8.5.4 / 6

缩小转换是隐式转换

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

1
假设这仅对内置类型的初始化有效,那么我看不出这会带来什么危害。当然,这可能会破坏一些代码。但应该易于修复。
Johan Kotlinski 2010年

1
@John Dibling:不,当目标类型可以精确表示该值时,初始化不会格式错误。(无论如何0已经是int。)
aschepler 2010年

2
@Nim:请注意,这只是在{花括号初始化程序中格式错误},并且它们的唯一旧用法是用于数组和POD结构。另外,如果现有代码在其所属位置具有显式强制转换,则不会中断。
aschepler,2010年

4
如工作文件所述,@ j_random_hackerint a = 1.0;仍然有效。
Johannes Schaub-litb 2010年

1
@litb:谢谢。实际上,我发现这是可以理解但令人失望的-恕我直言,从C ++开始就要求对所有缩小的转换使用显式语法要好得多。
j_random_hacker 2010年

Answers:


41

使用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)};
}

该代码在一个外部库中,一个文件中只有两次。我认为突破性的更改不会影响太多代码。不过,新手可能 感到困惑


9

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

这些还会缩小转化率吗?

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

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


1
我不明白为什么您会说这在代码中很少见。使用-1或INT_MAX代替USHRT_MAX之间的逻辑是什么?USHRT_MAX在2010年底是否没有处于限制中?

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};不应这样做?无论哪种方式,我都要初始化两个int的两个float,只是在一种情况下,两个float恰好是集合的成员。

在非恒定表达式导致转换变窄的情况下,即使(在特定实现上)所有源类型的值都可以在目标类型中表示并可以转换回其原始值,这似乎尤其严峻:

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

假设没有错误,大概是总能使转换明确的解决办法。除非您对宏做一些奇怪的事情,否则我认为数组初始化器只会出现在接近数组类型的地方,或者至少出现在表示类型的地方,这可能取决于模板参数。因此,如果冗长,强制转换应该很容易。


8
“如果我知道我正好有两个值,为什么要将它们放在数组中” -例如因为像OpenGL这样的API都需要它。
Georg Fritzsche 2010年

7

我遇到的一个实际实例:

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

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


1
所以float通过写作做到0.5f。;)
underscore_d

1
@underscore_d如果float是typedef或模板参数(至少没有损失精度),则不起作用,但要点是,编写的代码具有正确的语义,并在C ++ 11中成为错误。即,“重大变化”的定义。
杰德(Jed)

5

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

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

或CPPFLAGS(对于C ++编译器)(当然,这取决于您的构建系统或Makefile)
Mikolasan

4

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

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

struct char_t {
    char a;
}

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

产生缩小的转换误差(根据标准是正确的)。原因是,c并且d隐式地被提升为,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)或缩小转换错误(这可能是一件好事)也能正常工作。


我遇到了以下令人讨厌的废话:unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = { x & m };<-缩小{}内部的转换。真?因此,operator&还会将未签名的字符隐式转换为int吗?好吧,我不在乎,结果仍然保证是未签名的字符,啊。
卡洛·伍德

隐式整数转换”促销?
curiousguy

2

确实,这是一个重大突破,因为使用此功能的现实生活经验表明,由于将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}


1

看起来GCC-4.7不再提供错误以缩小转换范围,而是发出警告。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.