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

Posted

技术标签:

【中文标题】C++0x 中的缩小转换。只是我,还是这听起来像是一个重大变化?【英文标题】:Narrowing conversions in C++0x. Is it just me, or does this sound like a breaking change? 【发布时间】:2011-05-24 23:08:42 【问题描述】:

C++0x 将使以下代码和类似代码格式不正确,因为它需要将double 称为int窄化转换

int a[] =  1.0 ;

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


参考见n3225的8.5.4/6

窄化转换是隐式转换

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

【问题讨论】:

假设这仅对内置类型的初始化有效,我看不出这会有什么危害。当然,这可能会破坏一些代码。但应该很容易修复。 @John Dibling:不,当值可以由目标类型精确表示时,初始化不是格式错误的。 (而且0 已经是int 了。) @Nim:请注意,这仅在 大括号初始值设定项 中格式不正确,并且它们的唯一遗留用途是用于数组和 POD 结构。此外,如果现有代码在它们所属的地方有明确的强制转换,它也不会中断。 @j_random_hacker 正如工作文件所说,int a = 1.0; 仍然有效。 @litb:谢谢。实际上,我发现这可以理解但令人失望——恕我直言,从 C++ 开始就要求所有缩小转换的显式语法会更好。 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

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

unsigned short b[] =  -1, INT_MAX ;

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

【讨论】:

我不明白你为什么说这在代码中是不常见的。使用 -1 或 INT_MAX 而不是 USHRT_MAX 之间的逻辑是什么? USHRT_MAX 是否在 2010 年底没有达到峰值?【参考方案3】:

我遇到的一个实际例子:

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

数字字面量是隐含的double,这会导致提升。

【讨论】:

所以写成0.5f 使其成为float。 ;) @underscore_d 如果 float 是 typedef 或模板参数(至少不会损失精度),则不起作用,但关键是编写的代码使用正确的语义并成为C++11 出错。即,“重大变化”的定义。【参考方案4】:

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

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?

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

【讨论】:

“如果我知道我正好有两个值,为什么要把它们放在数组中” - 例如因为像 OpenGL 这样的 API 需要它。【参考方案5】:

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

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

【讨论】:

或 CPPFLAGS 在 C++ 编译器的情况下(当然这取决于你的构建系统或你的 Makefile)【参考方案6】:

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

我的代码看起来像个错误

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) 或缩小转换错误(这可能是一件好事)。

【讨论】:

我遇到了以下真的很烦人的废话:unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = x &amp; m ; 隐式整数转换”促销?【参考方案7】:

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

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

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

【讨论】:

另见:https://gcc.gnu.org/wiki/FAQ#Wnarrowing【参考方案8】:

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

【讨论】:

以上是关于C++0x 中的缩小转换。只是我,还是这听起来像是一个重大变化?的主要内容,如果未能解决你的问题,请参考以下文章

PDO rowCount() 支持的数据库

为啥我们使用堆来存储内存?

支持多线程程序的 C++0X 标准

C中的数组是通过指针使用的吗?

c ++,stl。将带有rgb颜色的字符串转换为3个int值?

使用情节提要时iOS目标c中的合并冲突是啥