在初始化列表中,非常数表达式的类型不能从'int'缩小为'unsigned long long'

问题描述 投票:1回答:1
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.
constructor bitset
1个回答
0
投票

失败的行使用列表初始化语法:

auto a = std::bitset<20>{cell}; //fails

此语法在C ++ 17标准的Section 11.6.4中定义。相关部分:

T类型的对象或引用的列表初始化定义如下:

...

(3.7)否则,如果T是类类型,则考虑构造函数。列举了适用的构造函数,并通过重载分辨率(16.3,16.3.1.7)选择了最佳的构造函数。 如果转换任何参数都需要缩小转换(请参见下文),则程序格式错误。

...

缩小转换是隐式转换

...

((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可能为负,因此我们的转换范围越来越窄。

我们可以通过将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

您现在可以问为什么列表初始化不允许缩小转换范围。我无法完全回答。我的理解是,通常认为隐式收窄是不希望的,因为它可能产生意想不到的后果,并且出于这个原因而将其排除在外。

© www.soinside.com 2019 - 2024. All rights reserved.