Apakah C ++ enum ditandatangani atau tidak?

107

Apakah C ++ enum ditandatangani atau tidak? Dan dengan ekstensi, apakah aman untuk memvalidasi input dengan memeriksa bahwa itu adalah <= nilai maks Anda, dan meninggalkan> = nilai min Anda (dengan asumsi Anda mulai dari 0 dan bertambah 1)?

Matt
sumber
Ketika kita menggunakan tipe enum dalam konteks yang membutuhkan tandanya, kita sebenarnya berbicara tentang mengubah enum menjadi tipe integral secara implisit. Standar C ++ 03 mengatakan ini dilakukan oleh Promosi Integral, tidak ada yang berhubungan dengan jenis enum yang mendasarinya. Jadi, saya tidak mengerti mengapa setiap jawaban di sini menyebutkan tipe yang mendasarinya tidak ditentukan oleh standar? Saya menjelaskan perilaku yang diharapkan di sini: stackoverflow.com/questions/24802322/…
JavaMan

Jawaban:

60

Anda tidak boleh mengandalkan representasi khusus apa pun. Baca link berikut . Juga, standar mengatakan bahwa itu adalah implementasi-didefinisikan tipe integral mana yang digunakan sebagai tipe dasar untuk enum, kecuali bahwa itu tidak boleh lebih besar dari int, kecuali beberapa nilai tidak dapat masuk ke dalam int atau int unsigned.

Singkatnya: Anda tidak dapat mengandalkan enum yang ditandatangani atau tidak.

zvrba
sumber
28
Jawaban Michael Burr (yang mengutip standar) sebenarnya menyiratkan bahwa Anda dapat mengandalkan penandatanganannya jika Anda mendefinisikan nilai enum sebagai negatif karena jenisnya dapat "mewakili semua nilai enumerator yang ditentukan dalam enumerasi".
Samuel Harmer
101

Mari kita pergi ke sumbernya. Inilah yang dikatakan dokumen standar C ++ 03 (ISO / IEC 14882: 2003) di 7.2-5 (Deklarasi pencacahan):

Tipe yang mendasari pencacahan adalah tipe integral yang dapat mewakili semua nilai pencacah yang ditentukan dalam pencacahan. Ini adalah implementasi-didefinisikan tipe integral mana yang digunakan sebagai tipe yang mendasari untuk pencacahan kecuali bahwa tipe yang mendasari tidak boleh lebih besar dari int kecuali nilai pencacah tidak dapat masuk ke dalam int atau unsigned int.

Singkatnya, kompilator Anda dapat memilih (jelas, jika Anda memiliki angka negatif untuk beberapa nilai ennumerasi Anda, itu akan ditandatangani).

Michael Burr
sumber
Bagaimana kita bisa menghindari tebakan compiler dan menyuruhnya menggunakan tipe unsigned yang mendasari ketika semua nilai enumerasi kecil, bilangan bulat positif? (Kami menangkap temuan UBsan karena kompiler mengambil int, dan int menderita overflow. Nilainya unsigned dan positif, dan penggunaan kami bergantung pada unsigned wrap untuk memberikan pengurangan atau "langkah negatif").
jww
@jww - itu akan tergantung pada kompiler apa yang sebenarnya Anda gunakan, saya rasa. Karena standar tidak menentukan jenis yang mendasari, dan menyerahkannya pada implementasi, maka perlu melihat dokumentasi alat Anda dan melihat apakah opsi ini memungkinkan. Jika Anda ingin menjamin perilaku tertentu dalam kode Anda, mengapa tidak menggunakan anggota enum yang Anda gunakan dalam ekspresi?
ysap
22

Anda tidak harus bergantung pada mereka yang ditandatangani atau tidak. Jika Anda ingin membuatnya ditandatangani atau tidak ditandatangani secara eksplisit, Anda dapat menggunakan yang berikut ini:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum
Adam Rosenfield
sumber
11
Hanya di masa depan C ++ 0x standar.
dalle
3
@dalle Microsoft compilator juga mengizinkan enum yang diketik msdn.microsoft.com/en-us/library/2dzy4k6e(v=vs.80).aspx
teodozjan
15

Anda tidak boleh mengandalkannya baik ditandatangani atau tidak ditandatangani. Menurut standar itu adalah implementasi yang ditentukan tipe integral mana yang digunakan sebagai tipe dasar untuk enum. Dalam kebanyakan implementasi, ini adalah integer bertanda.

Dalam C ++ 0x enumerasi yang diketik dengan kuat akan ditambahkan yang memungkinkan Anda untuk menentukan jenis enumerasi seperti:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Meskipun sekarang, beberapa validasi sederhana dapat dicapai dengan menggunakan enum sebagai variabel atau tipe parameter seperti ini:

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.
Matt
sumber
Saya pikir contoh kedua Anda agak membingungkan :)
Miral
5

Kompilator dapat memutuskan apakah enum ditandatangani atau tidak.

Metode lain untuk memvalidasi enum adalah dengan menggunakan enum itu sendiri sebagai tipe variabel. Sebagai contoh:

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.
Cristián Romo
sumber
5

Bahkan beberapa jawaban lama mendapat 44 suara positif, saya cenderung tidak setuju dengan semuanya. Singkatnya, saya tidak berpikir kita harus peduli underlying typedengan enum.

Pertama, tipe Enum C ++ 03 adalah tipe tersendiri yang tidak memiliki konsep tanda. Sejak dari standar C ++ 03dcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

Jadi ketika kita berbicara tentang tanda jenis enum, katakanlah ketika membandingkan 2 operan enum menggunakan <operator, kita sebenarnya berbicara tentang secara implisit mengubah jenis enum menjadi beberapa jenis integral. Tanda dari tipe integral inilah yang penting . Dan saat mengonversi enum ke tipe integral, pernyataan ini berlaku:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

Dan, tampaknya, jenis enum yang mendasari tidak ada hubungannya dengan Promosi Integral. Karena standar mendefinisikan Promosi Integral seperti ini:

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

Jadi, apakah jenis enum menjadi signed intatau unsigned intbergantung pada apakah signed intdapat berisi semua nilai enumerator yang ditentukan, bukan jenis enum yang mendasari.

Lihat pertanyaan terkait saya Tanda Jenis Enum C ++ Salah Setelah Mengonversi ke Jenis Integral

JavaMan
sumber
Itu penting saat Anda mengompilasi -Wsign-conversion. Kami menggunakannya untuk membantu menangkap kesalahan yang tidak diinginkan dalam kode kami. Tetapi +1 untuk mengutip standar, dan menunjukkan bahwa enum tidak memiliki jenis ( signedversus unsigned) yang terkait dengannya.
jww
4

Di masa mendatang, dengan C ++ 0x, enumerasi yang diketik dengan kuat akan tersedia dan memiliki beberapa keuntungan (seperti keamanan tipe, tipe dasar eksplisit, atau cakupan eksplisit). Dengan itu Anda bisa lebih yakin tentang tanda jenisnya.

Kris Kumler
sumber
4

Selain apa yang telah dikatakan orang lain tentang ditandatangani / tidak ditandatangani, inilah yang dikatakan standar tentang kisaran jenis yang disebutkan:

7.2 (6): "Untuk pencacahan di mana e (min) adalah pencacah terkecil dan e (maks) adalah yang terbesar, nilai pencacahan adalah nilai-nilai jenis yang mendasari dalam rentang b (min) hingga b (maks ), di mana b (min) dan b (max) adalah, masing-masing, nilai terkecil dan terbesar dari bitfield terkecil yang dapat menyimpan e (min) dan e (max). Dimungkinkan untuk menentukan enumerasi yang memiliki nilai tidak ditentukan oleh salah satu enumeratornya. "

Jadi contohnya:

enum { A = 1, B = 4};

mendefinisikan tipe enumerasi di mana e (min) adalah 1 dan e (max) adalah 4. Jika tipe yang mendasarinya ditandatangani int, maka bitfield terkecil yang dibutuhkan memiliki 4 bit, dan jika int dalam implementasi Anda adalah pelengkap dua maka rentang valid dari enumnya adalah -8 hingga 7. Jika tipe yang mendasarinya tidak bertanda tangan, maka ia memiliki 3 bit dan kisarannya adalah 0 hingga 7. Periksa dokumentasi compiler Anda jika Anda peduli (misalnya jika Anda ingin memasukkan nilai integral selain enumerator ke jenis yang dicacah, maka Anda perlu mengetahui apakah nilainya berada dalam kisaran pencacahan atau tidak - jika tidak, nilai enumerasi yang dihasilkan tidak ditentukan).

Apakah nilai-nilai tersebut merupakan masukan yang valid untuk fungsi Anda mungkin merupakan masalah yang berbeda dari apakah nilai-nilai tersebut merupakan nilai yang valid dari jenis yang disebutkan. Kode pengecekan Anda mungkin mengkhawatirkan tentang yang pertama daripada yang terakhir, sehingga dalam contoh ini setidaknya harus memeriksa> = A dan <= B.

Steve Jessop
sumber
0

Periksa dengan std::is_signed<std::underlying_type+ enum tercakup default keint

https://en.cppreference.com/w/cpp/language/enum menyiratkan:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

GitHub upstream .

Kompilasi dan jalankan:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

Keluaran:

0

Diuji pada Ubuntu 16.04, GCC 6.4.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber