C++ 自动类型扣除未按预期工作

C++ 自动类型扣除未按预期工作,c++,c++11,visual-c++,auto,C++,C++11,Visual C++,Auto,这很像这个问题 然而,有一个子问题是,为什么编译器在一种情况下诊断警告,在另一种情况下诊断错误,并且使用完全相同的表达式 我非常喜欢在auto var=中使用auto“type”,但MSVC 2015 CTP给出了我代码中的一个错误 问题是我正在auto-ing一个short类型的表达式,但有时它会升级为int 这是一个MCVE: struct MY_COORD { short X; short Y; }; using t_crd = MY_COORD; void call_test ( t

这很像这个问题 然而,有一个子问题是,为什么编译器在一种情况下诊断警告,在另一种情况下诊断错误,并且使用完全相同的表达式

我非常喜欢在
auto var=
中使用
auto
“type”,但MSVC 2015 CTP给出了我代码中的一个错误

问题是我正在
auto
-ing一个
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 };
   ...
}
MY_COORD pos=ResultFromFunction();
自动行=位置Y;
对于(自动i=(行-行);i
短x=1,y=2;
静态断言(
std::是相同的{},
减去两个短字符得到的是'int',而不是'short'
);
//这就是C++中的整体提升工作的方式
//小于'int'的几乎总是升级为'int'
INTA=3,b=4;
//如果使用“new”{}样式初始化
//进行缩小转换是错误的:
//短z1={b-a};
//但如果不使用{},则不会:
短z2=b-a;
(无效)x;(无效)y;(a)无效;(b)无效;(无效)z2;//阻止未使用的var警告。

您使用的
auto
变量大部分都是
int
s,因为它们通常是两个
short
s之间的差异。然后继续在各种上下文中将它们转换为
short
s——有时在编译器发出警告的上下文中,有时在执行缩小转换时出错

short
替换
auto
时,转换警告和错误会消失

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


一个典型的例子是
intx=(无符号)-1;if(x算术运算符在执行操作之前总是将小于
int
的整数类型升级为
int
。这是C++11标准的
[expr]/10
中规定的:

许多需要算术或枚举类型操作数的二进制运算符会导致转换并产生 以类似的方式创建结果类型。目的是生成一个公共类型,它也是结果的类型。 这种模式称为通常的算术转换

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

整数类型的PR值,而不是其整数转换的
bool
char16\u t
char32\u t
wchar\u t
秩小于
int
的秩可以转换为
int
类型的PR值,如果
int
可以表示所有 源类型的值;否则,源prvalue可以转换为类型为
unsigned int
的prvalue

由于
short
的秩保证小于
int
的秩,因此
行-行
的两个操作数都提升为
int
,从而使表达式
行-行
成为
int
的一种类型


关于窄化转换,标准(在
[dcl.init.aggr]
中)规定,在聚合初始化中,如果表达式需要窄化转换,则程序的格式是错误的。编译器可以根据自己的喜好对其进行任意诊断。IIRC,在
t_crd crd5{x5,y5}等简单情况下
,clang 3.5在与默认警告设置一起使用时发出错误,而g++4.9.2在与默认警告设置一起使用时发出警告。无论哪种方式。MSVC的行为,即使有点奇怪,也是符合标准的。

如果确实需要特定类型,只需指定类型。@Jerry Coffin:我希望它跟踪该类型(如果它发生变化)。如果它在这个简单的情况下不起作用,那么我就不能在更复杂的情况下使用它。。听起来您可能需要类似于(decltype(rows)I=0;的
?明显的替代方法是使用函数模板,因此它只会变成
t
(或您喜欢的任何名称)@Jerry Coffin:是的,但这需要更多的打字(和思考),我很懒:)但真正的问题是,我的代码正确还是MSVC正确?你能发布一个吗?谢谢,这很奇怪。但它没有解释为什么我在使用auto时收到警告/错误,而不是在使用short时收到警告/错误。@Edwin它怎么不正确?你的
auto
变量的类型是
int
,因为它们与rhs的类型相同。将它们更改为
short
使它们成为不同的类型,随后的
int
short
转换警告和错误当然会消失。感谢进一步的解释,现在对我来说更有意义。您的诊断参数使这成为最完整的答案。
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.