сохранить тип перечисления в битовом поле [dcl.enum] [class.bit] - PullRequest
2 голосов
/ 21 апреля 2019

В приведенном ниже коде переменная типа перечисления сохраняется в битовом поле. Насколько я понимаю, переменная может быть извлечена без потери данных. Однако это не сработает.

Соответствует ли приведенная ниже мощность стандарту?

#include<iostream>
using namespace std;
const int M=3, UL=(1<<M)-1;    // range 0 .. 7, fits into 3 bits
enum class ec { e_min=0, e_max=UL};
// in [decl.enum] (8): b_min==0 && b_max==7==2^M-1
struct bitFieldType {
    ec data : M;  // 3 bits
};
int main(){
  bitFieldType bf;
  for (int c=0; c<=UL; ++c){;
    ec enumIn { static_cast<ec>(c) }; // initialize enumeration type 
    bf.data = enumIn;                 // copy into bit-field
    ec enumOut{bf.data};              // retrieve enumeration type from bit-field
    cout<<static_cast<int>(enumIn) <<" "<< static_cast<int>(enumOut)
    << " " << (bf.data==enumIn) <<"\n";
  }
}

[dcl.enum] (8): «Размер наименьшего битового поля достаточно большой, чтобы вместить все значения типа перечисления max (M, 1), если b min равно нулю ... ". [class.bit] (4) «Значение перечислителя сохраняется в битовом поле того же тип перечисления и количество битов в битовом поле достаточно велики хранить все значения этого типа перечисления (10.2), оригинал значение перечислителя и значение битового поля сравнивать равные. "

Если так, то почему вывод выглядит так?

clang++ -Wall -fsanitize=undefined -pedantic -std=c++17 bitf.cpp && ./a.out
0 0 1
1 1 1
2 2 1
3 3 1
4 -4 0
5 -3 0
6 -2 0
7 -1 0
clang++ --version
clang version 9.0.0 (trunk 351995)
Target: x86_64-unknown-linux-gnu

EDIT: добавлен static_cast <> (), так что код компилируется с заменой 'enum class' на простой 'enum'.

При использовании простого enum вместо enum class вывод будет таким, как ожидалось. Кроме того, с одним дополнительным битом в битовом поле вывод будет таким, как ожидалось.

Ответы [ 2 ]

1 голос
/ 21 апреля 2019

Ваш ec, находящийся в области видимости, имеет фиксированный базовый тип int, поэтому его значения равны , все значения int, а ваше битовое поле не равно достаточно широк, чтобы гарантировать что-либо. На практике это, вероятно, интерпретируется как битовое поле Signed , которое должно иметь ширину 4, чтобы хранить значения 4–7 (даже если правило о подписи явно не имеет определить подпись типа перечисления как подписи его базового типа).

0 голосов
/ 21 апреля 2019

Я читаю это слишком быстро.У вас есть большие проблемы, чтобы начать с класса enum для начала.Как ни странно, если я уберу «класс» из объявления enum, предупреждение исчезнет;

Безопасно ли использовать enum в битовом поле?

g++     foo.cpp   -o foo
foo.cpp:7:15: warning: ‘bitFieldType::data’ is too small to hold all values of ‘enum class ec’
     ec data : M;  // 3 bits
               ^
foo.cpp: In function ‘int main()’:
foo.cpp:12:19: error: cannot convert ‘int’ to ‘ec’ in initialization
     ec enumIn { c };           // initialize enumeration type
                   ^
<builtin>: recipe for target 'foo' failed
make: *** [foo] Error 1

g++ --version
g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Оказывается, тип по умолчанию для перечислимого класса int , что объясняет расширение знака.Просто укажите тип, и это нормально, но все равно компилируется с предупреждением.

#include<iostream>
using namespace std;
const int M=3, UL=(1<<M)-1;    // range 0 .. 7, fits into 3 bits
enum class ec : unsigned int { e_min=0, e_max=UL};
// in [decl.enum] (8): b_min==0 && b_max==7==2^M-1
struct bitFieldType {
    ec data : M;  // 3 bits
};
int main(){
  bitFieldType bf;
  for (int c=0; c<=UL; ++c){;
    ec enumIn { (ec)(c) };           // initialize enumeration type
    bf.data = enumIn;          // copy into bit-field
    ec enumOut{bf.data};       // retrieve enumeration type from bit-field
    cout<<static_cast<int>(enumIn) <<" "<< static_cast<int>(enumOut)
    << " " << (bf.data==enumIn) <<"\n";
  }
}
/*
0 0 1
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
6 6 1
7 7 1
*/
...