неконстантное выражение не может быть сужено от типа 'int' до 'unsigned long long' в списке инициализатора - PullRequest
1 голос
/ 24 февраля 2020
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.

1 Ответ

2 голосов
/ 08 апреля 2020

В ошибочной строке используется синтаксис инициализации списка:

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

Этот синтаксис определен в Разделе 11.6.4 стандарта C ++ 17. Соответствующая часть:

Список-инициализация объекта или ссылки типа 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 a constexpr, который сильнее const. Тогда как const означает только то, что значение не должно изменяться, constexpr означает, что значение доступно во время компиляции:

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

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

Теперь вы можете спросить, почему инициализация списка не позволяет сужать преобразование. Я не могу полностью ответить на это. Насколько я понимаю, неявное сужение обычно считается нежелательным, поскольку оно может иметь непредвиденные последствия, и что по этой причине оно было исключено.

...