自动类型扣除不按预期工作

时间:2015-03-16 01:28:16

标签: c++ c++11 visual-c++ auto

这就像这个问题Why must a short be converted to an int before arithmetic operations in C and C++? 但是有一个子问题,为什么编译器在一种情况下诊断警告而在另一种情况下诊断错误,具有完全相同的表达式。

我非常喜欢使用auto'类型'与在auto var = ...中一样,但是MSVC 2015 CTP从我的代码中给出了错误。

我的问题是auto - 表达式为short但有时会被提升为int

这是一个MCVE:

struct MY_COORD { short X; short Y; };
using t_crd = MY_COORD; 

void call_test ( t_crd x )  {}

int main()
{
    t_crd    crd { 10 ,20 };

    auto     x5 = crd.X - crd.Y;
    auto     y5 = crd.Y - crd.X;
    t_crd    crd5 { x5 ,y5 };       // (1)
    call_test( t_crd{ x5 ,y5 } );   // (2)
}

第(1)和(2)行的消息分别为:

 warning C4838: conversion from 'int' to 'short' requires a narrowing conversion
 error C2397: conversion from 'int' to 'short' requires a narrowing conversion

我认为这段代码还可以,但它不符合MSVC 2015 CTP编译器(但Intellisense没有表明它)。 我错过了一些模糊的小规则,使MSVC正确吗?

如果是这样,为什么同一个表达式在一个案例中为警告,在另一个案例中为错误

我想在循环变量的初始化中使用它,如下所示:

MY_COORD pos = ResultFromFunction();
auto rows = pos.Y;
for ( auto i = (rows - rows); i < rows; ++i )
{
   coord c{ 0 ,i };
   ...
}

2 个答案:

答案 0 :(得分:6)

在执行操作之前,算术运算符始终将小于int的整数类型提升为int。这是在C ++ 11标准的[expr]/10中强制执行的:

  

许多期望算术或枚举类型操作数的二元运算符会导致转换和产生   结果类型以类似的方式。目的是产生一个通用类型,它也是结果的类型。   此模式称为通常的算术转换

适用于您的案例的子条款规定rows - rows中的操作数将进行整体提升。特别是,根据[conv.prom]/1

  

boolchar16_tchar32_twchar_t整数转换以外的整数类型的prvalue   如果int可以代表所有int,则排名小于int的排名可以转换为unsigned int类型的公开值   源类型的值;否则,源prvalue可以转换为short类型的prvalue。

由于int的排名保证小于rows - rows的排名,因此int的两个操作数都会提升为rows - rows,表达式为{{ 1}}一种int

关于缩小转换,标准指定(在[dcl.init.aggr]中),在聚合初始化中,如果表达式需要缩小转换,则程序格式不正确。无论如何,编译器都可以自由地诊断它。 IIRC,在t_crd crd5 { x5 ,y5 };之类的简单情况下,clang 3.5发出错误,而g ++ 4.9.2在与默认警告设置一起使用时发出警告。无论哪种方式。 MSVC的行为,即使有点奇怪,也符合标准。

答案 1 :(得分:2)

short x=1, y=2;
static_assert(
  std::is_same<int, decltype(x-y)>{},
  "subtracting two shorts gets an `int`, not a `short`"
);
// that is how integral promotion works in C++ -- things
// smaller than `int` promote to `int` almost always
int a=3,b=4;
// if you use the "new" {} style initialization
// it is an error to have a narrowing conversion:
//short z1 = {b-a};
// but not if you don't use {}:
short z2 = b-a;
(void)x;(void)y;(void)a;(void)b;(void)z2; // block unused var warnings.

live example

您所做的变量auto大多数都是int个,因为它们通常是两个short之间的差异。然后,您可以在各种上下文中将它们转换为short - 有时在编译器发出警告的上下文中,有时在执行缩小转换时出错。

当您将auto替换为short时,转换警告和错误就会消失。

请注意,short-short很容易溢出short。如果是,则结果是未定义的行为(未定义带符号的溢出)。在实践中,硬件可以执行mod 2 ^ n,但即使在2s补充硬件上,优化器也可以自由地假设不会发生溢出,这可能会在优化期间导致代码看似疯狂的更改。

一个典型的例子是int x = (unsigned)-1; if (x<0) std::cout << "not printed\n"; std :: cout&lt;&lt; x&lt;&lt; “== -1 \ n”; , where the branch of x&lt; 0 can be eliminated as "impossible" by an optimizer (as we converted x`来自无符号值,除非溢出,否则不能为负,如果溢出则是未定义的行为,所以任何事情都会发生)

因此,通过将auto更改为short,您实际上将警告和错误所讨论的未定义行为移动到编译器不会发出警告或生成错误的位置。问题仍然存在。