提问人:24n8 提问时间:2/24/2020 最后编辑:f9c69e9781fa19421144847349553424n8 更新时间:4/8/2020 访问量:18260
在初始值设定项列表中,non-constant-expression 不能从类型“int”缩小到“unsigned long long”
non-constant-expression cannot be narrowed from type 'int' to 'unsigned long long' in initializer list
问:
int main(int argc, char const *argv[])
{
int x = 4;
int y = 2;
const int cell = x/y;
auto a = std::bitset<20>{cell}; //fails
auto b = std::bitset<20>(cell); //works
}
为什么不允许我在这里使用大括号构造,而是使用括号构造?如果是 ,则两者都将编译。std::bitset
cell
constexpr
编译错误:
test.cpp:21:29: error: non-constant-expression cannot be narrowed from type 'int' to 'unsigned long long' in initializer list [-Wc++11-narrowing]
auto a = std::bitset<20>{x*y}; //fails
^~~
test.cpp:21:29: note: insert an explicit cast to silence this issue
auto a = std::bitset<20>{x*y}; //fails
^~~
static_cast<unsigned long long>( )
1 error generated.
答:
8赞
f9c69e9781fa194211448473495534
4/8/2020
#1
失败的行使用列表初始化语法:
auto a = std::bitset<20>{cell}; //fails
此语法在 C++17 标准的第 11.6.4 节中定义。相关部分:
对象或类型的引用的列表初始化定义如下:
T
...
(3.7) 否则,如果是类类型,则考虑构造函数。枚举了适用的构造函数,并通过重载解析(16.3、16.3.1.7)选择最佳构造函数。如果需要缩小转换(见下文)来转换任何参数,则程序格式不正确。
T
...
缩小转换是隐式转换
...
(7.4) 从整数类型或无作用域枚举类型转换为无法表示原始类型所有值的整数类型,除非源是常量表达式,其值在整数升级后将适合目标类型。
这让我们更好地了解正在发生的事情:
// Works, no narrowing check, implicit conversion.
std::bitset<20> a(2);
std::bitset<20> b(-1);
std::bitset<20> c(cell);
// Works, 2 can be converted without narrowing
std::bitset<20> d{2};
// Fails, -1 cannot be converted without narrowing
std::bitset<20> e{-1};
// Fails, compiler does not understand cell can be converted without narrowing
std::bitset<20> f{cell};
在程序中,编译器不理解这是一个常量表达式。它检查可用的构造函数,并看到它必须从 转换为 。它认为这可能是负面的,因此我们有一个缩小的转换。cell
std::bitset
int
unsigned long long
int
我们可以通过制作一个比 更强的 来解决此问题。While only 表示不应更改该值,表示该值在编译时可用:cell
constexpr
const
const
constexpr
constexpr int x = 4;
constexpr int y = 2;
constexpr int cell = x / y;
auto a = std::bitset<20>{cell}; // works
您现在可以问为什么列表初始化不允许缩小转换范围。我不能完全回答这个问题。我的理解是,隐性缩小通常被认为是不可取的,因为它可能会产生意想不到的后果,因此它被排除在外。
评论
0赞
24n8
4/25/2020
啊,我不知道 an 到 an 是一个缩小的转换。这是因为可以是负面的吗?如果它是无符号的而不是普通的,那么转换似乎会扩大而不是缩小。int
unsigned long long
int
int
int
0赞
f9c69e9781fa194211448473495534
4/25/2020
是的,它正在缩小,因为无法表示负值。使用时,您的原始程序编译良好。unsigned int
0赞
Shayna
11/22/2021
@f9c69e9781fa194211448473495534什么?不,这是一个缩小的转换,因为在 99% 的编译器上,是 32 位整数,是 64 位整数。从到不是一个缩小的转换;表示任何大小整数的有符号或无符号版本所需的位数是相同的,唯一的区别是如何解释这些位。有符号 32 位整数的最后一位表示剩余的 31 位是否为负数,这就是为什么有符号整数限制是2147483648而不是4294967296(2^31 与 2^32),这是唯一的区别。int
long long
signed
unsigned
0赞
f9c69e9781fa194211448473495534
11/23/2021
@Shayna我不明白你的意思。我对C++标准的理解是,缩小转换是关于可表示的值范围,而不是所需的位数:“缩小转换是从整数类型或无作用域枚举类型到无法表示原始类型所有值的整数类型的隐式转换”(open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3035.pdf#page=218)。无符号类型不能表示有符号值。
1赞
Shayna
11/24/2021
@f9c69e9781fa194211448473495534我的印象是,缩小转换意味着试图将数据从一种类型拟合到一种更窄的类型中:一种在逻辑上无法包含原始数据的类型,因为它太短了,例如 32>16、16>8 等,导致溢出的位被切断——当从 signed>unsigned 时,情况并非如此。我不知道的是,该标准将该定义扩展到符号转换的丢失。当我尝试编译它时,编译器抱怨两个缩小转换,long long>int 和 signed>unsigned。我的错!你是对的。
评论