非常量表达式不能在初始化列表中从类型 '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.

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

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

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

List-initialization of an object or reference of type T is defined as follows:

...

(3.7) Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (16.3, 16.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

...

A narrowing conversion is an implicit conversion

...

(7.4) from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.

这让我们更好地了解正在发生的事情:

// 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 成为比 const 更强的 constexpr 来解决这个问题。 const 仅表示不应更改该值,而 constexpr 表示该值在编译时可用:

  constexpr int x = 4;
  constexpr int y = 2;
  constexpr int cell = x / y;

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

您现在可以问为什么列表初始化不允许缩小转换。我不能完全回答这个问题。我的理解是,隐式缩小通常被认为是不可取的,因为它可能会产生意想不到的后果,因此被排除在外。