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.