İleri C ++ bir numaralandırma bildirme


264

Aşağıdaki gibi bir şey yapmaya çalışıyorum:

enum E;

void Foo(E e);

enum E {A, B, C};

derleyici reddeder. Google'a hızlıca baktım ve fikir birliği "yapamazsın" gibi görünüyor, ama nedenini anlayamıyorum. Birisi açıklayabilir mi?

Açıklama 2: Enum adı verilen bir sınıfta özel yöntemlere sahip olduğum için bunu yapıyorum ve enum'un değerlerinin açığa çıkmasını istemiyorum - bu nedenle, örneğin, E'nin şu şekilde tanımlandığını bilmesini istemiyorum

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

çünkü X projesi, kullanıcılarımın bilmesini istediğim bir şey değil.

Bu nedenle, enum bildirmek istedim, böylece özel yöntemleri başlık dosyasına koyabilir, enum'u dahili olarak cpp'de bildirebilir ve yerleşik kütüphane dosyasını ve başlığı insanlara dağıtabilirim.

Derleyiciye gelince - bu GCC.


Uzun yıllar bu ve bir şekilde StackOverflow beni geri çekti;) Postmortem bir öneri olarak - bunu özellikle açıkladığınız senaryoda yapmayın . Ben soyut bir arabirim tanımlamak ve bu kullanıcılara göstermek ve numaralandırma tanımını ve diğer tüm uygulama ayrıntılarını, başka hiç kimsenin benim tarafımda görmediği iç uygulama ile tutmak istiyorum ve ne zaman ne olursa olsun yapmak ve kullanıcılar ne zaman tam kontrol sahibi olmak istiyorum herhangi bir şey.
RnR

Yanıtlar:


217

Numaralandırmanın iletilmemesinin nedeni, değerleri bilmeden, derleyicinin numaralandırma değişkeni için gereken depolamayı bilememesidir. C ++ Derleyicisinin gerçek depolama alanını, belirtilen tüm değerleri içermesi için gereken boyuta göre belirlemesine izin verilir. Görünen her şey ileri bildirim ise, çeviri birimi hangi depolama boyutunun seçileceğini bilemez - bir char veya int veya başka bir şey olabilir.


ISO C ++ Standardının 7.2.5 Bölümünden:

Temel tipi bir numaralandırma numaralandırma tanımlanan tüm Numaralayıcı değerlerini temsil tamamlayıcı türüdür. Bu uygulama tanımlı integral tip yatan tür daha büyük olmayacak şekilde, başka bir numaralandırma için temel tipi olarak kullanılmakta olan intbir numaralayıcısı değeri sürece uygun değildir intya da unsigned int. Eğer Numaralayıcı listesi boş numaralandırma değeri 0 değerine sahip tek bir numaralandırıcıyı varmış gibi, altta yatan tip sizeof()bir numaralandırma türü, sayım tip bir nesnenin ya da bir numaralandırıcıya uygulanan değeri sizeof()uygulanan altta yatan tür.

Yana Arayan işlevine doğru kurulum için çağrı yığını parametrelerinin boyutlarını bilmelidir, bir numaralandırma listesinde numaralandırma sayısı işlevi prototip önce bilinmelidir.

Güncelleme: C ++ 0X'te enum türlerini bildiren foreward için bir sözdizimi önerildi ve kabul edildi. Teklifi http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf adresinde görebilirsiniz.


29
-1. Akıl yürütmeniz doğru olamaz - aksi halde, neden "C sınıfı" beyan etmenize izin verilir? ve sonra C? yi tam olarak tanımlamadan önce bir C alan veya döndüren bir fonksiyon prototipi bildirin.
j_random_hacker

112
@j_random: Bir sınıfı tam olarak tanımlanmadan kullanamazsınız - yalnızca o sınıfa bir işaretçi veya bir başvuru kullanabilirsiniz ve bunun nedeni, boyutlarının ve işlem biçimlerinin sınıfın ne olduğuna bağlı olmamasıdır.
RnR

27
Bir sınıf nesnesine yapılan başvurunun veya işaretçinin boyutu derleyici tarafından ayarlanır ve nesnenin gerçek boyutundan bağımsız olarak işaretçilerin ve başvuruların boyutudur. Enum bir nesnedir ve derleyicinin doğru depolamaya erişmesi için boyutu gereklidir.
KJAWolf

16
Mantıksal olarak, tıpkı sınıflarla yapabileceğimiz gibi, ileriye dönük enum ilanlarımız olsaydı, işaretçiler / numaralandırmalara referanslar bildirebilirdi. Sadece enums işaretçilerle uğraşmak değil :)
Pavel Minaev

20
Bu tartışmanın uzun zaman önce sona erdiğini biliyorum, ama burada @j_random_hacker ile hizalamak zorundayım: buradaki sorun işaretçi veya eksik türlere referans değil, bildirimlerde eksik türlerin kullanımı ile ilgili. Yapılması yasal olduğu için struct S; void foo(S s);( foosadece tanımlandığını, tanımlanmadığını unutmayın), o zaman da yapamamamız için bir neden yoktur enum E; void foo(E e);. Her iki durumda da, boyuta gerek yoktur.
Luc Touraille

199

C ++ 11'den beri numaralandırmaların ileri bildirimi mümkündür. Daha önce, numaralandırma türlerinin ileri bildirilmemesinin nedeni numaralandırmanın boyutunun içeriğine bağlı olmasıdır. Numaralandırma boyutu uygulama tarafından belirtildiği sürece, ileri bildirilebilir:

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.

1
Bu özellik için herhangi bir derleyici desteği var mı? GCC 4.5 öyle görünmüyor :(
rubenvb


Ben enum32_t arıyordum ve cevabınız enum XXX ile: uint32_t {a, b, c};
fantastory

Kapsamlı numaralandırmalar (enum sınıfı) C ++ 11 uygulandığını düşündüm? Öyleyse, C ++ 0X'te nasıl yasaldırlar?
Terrabits

1
C ++ 0x, resmi olarak standartlaştırılmadan önce @Terrabits adlı C ++ 11 için çalışma adıdır. Mantık, bir özelliğin güncellenmiş bir standarda dahil edildiği biliniyorsa (veya büyük olasılıkla), standart resmen serbest bırakılmadan önce bu özelliğin kullanımının çalışma adını kullanma eğiliminde olmasıdır. (Örneğin, 2011'de resmi standartlaştırmadan önce C ++ 11 özelliklerini destekleyen derleyiciler C ++ 0x desteğine, resmi standartlaştırmadan önce C ++ 17 özelliklerini destekleyen derleyicilere C ++ 1z desteği vardı ve C ++ 20 özelliklerini destekleyen derleyiciler şu anda (2019) C ++ 2a desteğine sahip.)
Justin Time - Monica'yı eski

79

Son gelişmelerden yola çıkarak buraya güncel bir cevap ekliyorum.

Depolama türünü aynı anda bildirdiğiniz sürece C ++ 11'de bir numaralandırma iletebilirsiniz. Sözdizimi şöyle görünür:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

Aslında, fonksiyon hiçbir zaman numaralandırma değerlerine değinmiyorsa, o noktada tam bir bildirime ihtiyacınız yoktur.

Bu, G ++ 4.6 ve sonraki sürümler ( -std=c++0xveya -std=c++11daha yeni sürümlerde) tarafından desteklenir . Visual C ++ 2013 bunu destekler; Önceki sürümlerde henüz çözemediğim bir çeşit standart dışı destek var - Basit bir ileri bildirimin yasal olduğunu, ancak YMMV'nin bazı önerilerini buldum.


4
+1 çünkü bu, beyanınızdaki türünüzü ve tanımınızı belirtmeniz gereken tek cevaptır.
turoni

En azından doğru hatırlıyorsam , erken MSVC kısmi destek C ++ / CLI enum classC ++ uzantısı (önce C ++ 11 farklı önce enum class) olarak desteklendiğini düşünüyorum. Derleyici, bir numaralandırmanın temel türünü belirtmenize izin verdi, ancak enum classbildirilen numaralandırmaları desteklemedi veya iletmedi ve bir numaralandırıcının numaralandırmanın kapsamıyla nitelendirilmesinin standart olmayan bir uzantı olduğu konusunda sizi uyardı. Kabaca C ++ 11 altta yatan türü belirtmekle aynı şekilde çalıştığını hatırlıyorum, daha fazla can sıkıcı hariç, uyarıyı bastırmak zorunda kaldım.
Justin Time - Monica'yı

30

C ++ 'da bir şeyleri ileri sürmek çok yararlıdır, çünkü derleme süresini önemli ölçüde hızlandırır . Sen ileri dahil C bazı şeyleri ++ bildirebilirsiniz: struct, class, function, vb ...

Ancak ileri bir enumC ++ bildirmek ?

Hayır yapamazsın.

Ama neden izin vermiyorsun? İzin verildiyse, enumtürünüzü başlık dosyanızda ve enumdeğerlerinizi kaynak dosyanızda tanımlayabilirsiniz . İzin verilmeli gibi geliyor değil mi?

Yanlış.

C ++ 'da, enumC # (int)' de olduğu gibi varsayılan bir türü yoktur . C ++ 'da enumtürünüz derleyici tarafından sizin için sahip olduğunuz değer aralığına uyan herhangi bir tür olarak belirlenecektir enum.

Bu ne anlama geliyor?

Bu enum, enumtanımlanmış olan değerlerin tümüne sahip olana kadar temelinizin türünün tam olarak belirlenemeyeceği anlamına gelir . Hangi adamın sizin beyan ve tanımını ayıramazsınız enum. Ve bu nedenle bir enumC ++ 'da bildiremezsiniz .

ISO C ++ standardı S7.2.5:

Bir numaralandırmanın altta yatan türü, numaralandırmada tanımlanan tüm numaralandırıcı değerlerini temsil edebilen ayrılmaz bir tiptir. Bu uygulama tanımlı integral tip yatan tür daha büyük olmayacak şekilde, başka bir numaralandırma için temel tipi olarak kullanılmakta olan intbir numaralayıcısı değeri sürece uygun değildir intya da unsigned int. Numaralayıcı-Liste boşsa numaralandırma değeri 0 değerine sahip tek bir numaralandırıcıyı varmış gibi, altta yatan tip sizeof()bir numaralandırma türü, sayım tip bir nesnenin ya da bir numaralandırıcıya uygulanan değeri sizeof()uygulanan altta yatan tür.

sizeofİşleci kullanarak numaralandırılmış bir türün boyutunu C ++ 'da belirleyebilirsiniz . Numaralandırılmış türün boyutu, altta yatan türün boyutudur. Bu şekilde derleyicinizin sizin için hangi türü kullandığını tahmin edebilirsiniz enum.

enumAçık bir şekilde bunun türünü belirtirseniz ne olur :

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Sonra ileri beyan edebilir enummisiniz?

Hayır. Ama neden olmasın?

An türünü belirtmek enumaslında mevcut C ++ standardının bir parçası değildir. Bir VC ++ uzantısıdır. Yine de C ++ 0x bir parçası olacak.

Kaynak


14
Bu cevap şimdi birkaç yıldır güncel değil.
Tom

Zaman hepimizi aptal yerine koyar. Yorumunuz artık birkaç yıldır güncel değil; cevap on yıl!
pjcard

14

[Cevabım yanlış, ama yorumları burada faydalı olduğu için burada bıraktım].

Farklı numaralandırma türlerinin işaretleyicilerinin aynı boyutta olması garanti edilmediğinden, ileri doğru bildirilen numaralandırmalar standart değildir. Derleyicinin, bu türle hangi boyut işaretlerinin kullanılabileceğini bilmek için tanımı görmesi gerekebilir.

Pratikte, en azından tüm popüler derleyicilerde, numaralandırıcılara işaretçiler tutarlı bir boyuttadır. Numaralandırmaların ileri bildirimi, örneğin Visual C ++ tarafından bir dil uzantısı olarak sağlanır.


2
-1. Akıl yürütmeniz doğruysa, aynı akıl yürütme, sınıf türlerinin ileri bildirimlerinin, bu türlere işaretçiler oluşturmak için kullanılamayacağını, ancak bunu yapabileceğini ima eder.
j_random_hacker

6
+1. Akıl yürütme doğrudur. Bu özel durum sizeof (char *)> sizeof (int *) 'in bulunduğu platformlardır. Her ikisi de aralığa bağlı olarak bir numaralandırma için altta yatan türler olabilir. Sınıfların altında yatan türleri yoktur, bu yüzden benzetme yanlıştır.
MSalters

3
@ MSalters: Örnek: "struct S {int x;};" Şimdi, sizeof (S *) , diğer herhangi bir işaretçi-yapı boyutuna eşit olmalıdır , çünkü C ++, böyle bir işaretçinin S tanımından önce bildirilmesine ve kullanılmasına izin verir ...
j_random_hacker

1
@MSalters: ... sizeof (char *)> sizeof (int *) 'in kullanıldığı bir platformda, bu belirli yapı için böyle bir "tam boyutlu" işaretçi kullanmak verimsiz olabilir, ancak kodlamayı önemli ölçüde basitleştirir - ve tamamen aynı enum tipleri için bir şey yapılabilir ve yapılmalıdır.
j_random_hacker

4
Verilere işaretçiler ve işlevlere işaretçiler farklı boyutlarda olabilir, ancak veri işaretleyicilerinin gidiş-dönüş (başka bir veri işaretçisi türüne, ardından orijinaline geri dönmesi, yine de çalışması gerekir) olması gerektiğinden oldukça eminim. veri işaretçileri aynı boyuttadır.
Ben Voigt

7

Gerçekten de enumun ileri bir beyanı diye bir şey yoktur. Bir enum tanımlaması, enum kullanan diğer koda bağlı olabilecek herhangi bir kod içermediğinden, enum'u ilk bildirirken tamamen tanımlamak genellikle bir sorun değildir.

Numaralandırmanızın tek kullanımı özel üye işlevleriyse, numaralandırmanın kendisini bu sınıfın özel üyesi olarak alarak kapsülleme uygulayabilirsiniz. Numaralandırma hala bildirim noktasında, yani sınıf tanımında tam olarak tanımlanmalıdır. Bununla birlikte, bu özel üye işlevlerini orada bildirmek gibi daha büyük bir sorun değildir ve uygulama içlerinin bundan daha kötü bir şekilde ortaya çıkması değildir.

Uygulama ayrıntılarınız için daha derin bir gizliliğe ihtiyacınız varsa, bunu yalnızca saf sanal işlevlerden oluşan soyut bir arabirime ve arabirimi somut, tamamen gizlenmiş bir sınıf uygulamasına (devralma) ayırabilirsiniz. Sınıf örneklerinin oluşturulması bir fabrika veya arabirimin statik üye işlevi tarafından gerçekleştirilebilir. Bu şekilde, gerçek sınıf adı, özel işlevleri bir yana, açığa çıkmaz.


5

Sadece nedeninin aslında belirterek olduğu enum boyutu henüz ortaya ilanı sonrasında bilinmemektedir söyledi. Peki, bir işaretçiyi etrafından geçirmek veya ileri bildirilen yapı tanımının kendisinde de atıfta bulunulan bir yerden bir nesneye başvurabilmek için bir yapının ileri bildirimini kullanırsınız.

Bir enum bildirmek çok yararlı olmaz, çünkü enum by-value'ın içinden geçmek isterlerdi. Son zamanlarda bazı platformlarda char için int veya long için farklı boyutta işaretçiler kullandığını söyledim. Yani her şey enumun içeriğine bağlı.

Mevcut C ++ Standardı,

enum X;

(in 7.1.5.3/1). Ama nedeniyle gelecek yıl yanında C ++ Standart bana sorunu aslında ikna aşağıdakileri izin verir sahiptir altta yatan türü ile yapılacak:

enum X : int;

"Opak" numaralandırma bildirimi olarak bilinir. X'i aşağıdaki kodda değere göre bile kullanabilirsiniz . Ve onun numaralandırıcıları daha sonra numaralandırmanın daha sonraki bir bildirisinde tanımlanabilir. Bkz 7.2Geçerli çalışma taslağında.


4

Bunu şu şekilde yaparım:

[genel başlıkta]

typedef unsigned long E;

void Foo(E e);

[dahili başlıkta]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

FORCE_32BIT ekleyerek Econtent'in uzun bir süre derlenmesini sağlıyoruz, bu yüzden E ile değiştirilebilir.


1
Elbette bu, (A) E ve Econtent türlerinin farklı olduğu ve (B) LP64 sistemlerinde, sizeof (E) = 2 * sizeof (EContent) boyutunda olduğu anlamına gelir. Önemsiz düzeltme: ULONG_MAX, daha kolay okunabilir.
MSalters

2

Enum'unuzun başlık dosyanızda gerçekten görünmesini istemiyorsanız VE yalnızca özel yöntemlerle kullanıldığından emin değilseniz, bir çözüm pimpl ilkesine gitmek olabilir.

Sadece şunu bildirerek, sınıf içi başlıkların başlıklarda gizlenmesini sağlayan bir tekniktir:

class A 
{
public:
    ...
private:
    void* pImpl;
};

Daha sonra uygulama dosyanızda (cpp), iç kısımların temsili olacak bir sınıf beyan edersiniz.

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

Sınıf yapıcısında uygulamayı dinamik olarak oluşturmanız ve yıkıcıdan silmeniz ve genel yöntemi uygularken şunları kullanmanız gerekir:

((AImpl*)pImpl)->PrivateMethod();

Pimpl kullanmanın artıları var, biri sınıf başlığınızı uygulamasından ayırıyor, bir sınıf uygulamasını değiştirirken diğer sınıfları yeniden derlemeye gerek yok. Bir diğeri, başlıklarınızın çok basit olması nedeniyle derleme sürenizi hızlandırmasıdır.

Ancak bu, kullanmak için bir acıdır, bu yüzden kendinize, toplamanızın başlıkta özel olarak ilan edilmesinin bu kadar sorun olup olmadığını gerçekten sormalısınız.


3
yapı AImpl; struct A {private: AImpl * pImpl; };

2

Numaralandırmayı bir kurguya sarabilir, bazı kurucuları ekleyebilir ve dönüşümleri yazabilir ve bunun yerine yapıyı iletebilirsiniz.

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

Bu işe yarıyor gibi görünüyor: http://ideone.com/TYtP2



1

Bu çarpıştığından beri bir tür muhalefet var, bu yüzden standarttan ilgili bazı bitler. Araştırmalar, standardın ileriye yönelik beyanı gerçekten tanımlamadığını veya numaralandırmaların ileriye doğru beyan edilebileceğini veya bildirilemeyeceğini açıkça belirtmemektedir.

İlk olarak, dcl.enum, bölüm 7.2:

Bir numaralandırmanın altta yatan türü, numaralandırmada tanımlanan tüm numaralandırıcı değerlerini temsil edebilen ayrılmaz bir tiptir. Bir numaralandırıcının değeri int veya işaretsiz bir int'e sığmadığı sürece, altta yatan tipin int'den daha büyük olmaması dışında, uygulama tanımlı bir sayım için altta yatan tür olarak kullanıldığı tanımlanmıştır. Numaralandırma listesi boşsa, altta yatan tür, numaralandırmanın 0 değerine sahip tek bir numaralandırıcıya sahip olduğu gibidir. Bir numaralandırma türüne, numaralandırma türünün bir nesnesine veya numaralandırıcıya uygulanan sizeof () değeri, temel alınan türe uygulanır.

Dolayısıyla, altta yatan bir enum tipi, küçük bir kısıtlamayla uygulama tanımlıdır.

Ardından, ileri bildirimlerde herhangi bir standarda yaklaştığımız kadar yakın olan "eksik türler" (3.9) bölümüne dönüyoruz:

Bildirilen ancak tanımlanmayan bir sınıf veya bilinmeyen boyutta veya tamamlanmamış öğe türünde bir dizi, tam olarak tanımlanmamış bir nesne türüdür.

Bir sınıf türü ("sınıf X" gibi) bir çeviri biriminde bir noktada eksik olabilir ve daha sonra tamamlanabilir; "sınıf X" tipi her iki noktada da aynı tiptedir. Bir dizi nesnesinin bildirilen türü, tamamlanmamış sınıf türünden oluşan bir dizi olabilir ve bu nedenle de eksik olabilir; sınıf türü daha sonra çeviri biriminde tamamlanırsa, dizi türü tamamlanır; bu iki noktadaki dizi türü aynı türdür. Bir dizi nesnesinin bildirilen türü bilinmeyen boyutta bir dizi olabilir ve bu nedenle bir çeviri birimindeki bir noktada eksik ve daha sonra tamamlanabilir; bu iki noktadaki dizi türleri ("T'nin bilinmeyen sınırı dizisi" ve "N T dizisi") farklı tiplerdir. İşaretçinin bilinmeyen boyutta diziye veya typedef bildirimi ile bilinmeyen boyutta bir dizi olarak tanımlanan bir türe,

Böylece, standart hemen hemen ileriye doğru bildirilebilecek türleri ortaya koydu. Enum orada değildi, bu yüzden derleyici yazarları genellikle ileri bildirimi, altta yatan türünün değişken boyutu nedeniyle standart tarafından izin verilmediğini düşünüyorlar.

Bu da mantıklı. Numaralandırmalara genellikle değere sahip durumlarda başvurulur ve derleyicinin bu durumlarda depolama boyutunu gerçekten bilmesi gerekir. Depolama boyutu uygulama tanımlı olduğundan, birçok derleyici her bir enumun altta yatan türü için 32 bit değerleri kullanmayı seçebilir, bu noktada bunları bildirmek mümkün olur. İlginç bir deney, görsel stüdyoda bir enum bildirmeyi denemek, daha sonra ne olduğunu görmek için yukarıda açıklandığı gibi sizeof (int) 'den daha büyük bir alt türü kullanmaya zorlamak olabilir.


"enum foo" ya açıkça izin vermediğini unutmayın; 7.1.5.3/1'de (ancak her şeyde olduğu gibi, derleyici uyardığı sürece, yine de bu kodu derleyebilir)
Johannes Schaub - litb

İşaret ettiğiniz için teşekkürler, bu gerçekten ezoterik bir paragraf ve ayrıştırmam bir hafta sürebilir. Ama orada olduğunu bilmek güzel.
Dan Olson

Endişe yok. bazı standart paragraflar gerçekten garip :) iyi, ayrıntılı bir tür belirteci, bir tür belirttiğiniz bir şeydir, ancak aynı zamanda belirsiz hale getirmek için daha fazla bir şey belirtir. Örneğin, "X" yerine "struct X" veya yalnızca "Y" yerine "enum Y". bir şeyin gerçekten bir tür olduğunu iddia etmeniz gerekiyor.
Johannes Schaub - litb

böylece şu şekilde kullanabilirsiniz: "class X * foo;" X henüz ileriye doğru beyan edilmemişse. veya anlam ayrımı için bir şablonda "X: foo yazınız". veya "class link obj;" aynı kapsamda, aynı ada sahip sınıfı gölgeleyecek bir işlev "bağlantısı" varsa.
Johannes Schaub - litb

3.4.4'te, tür olmayan bir adın bir tür adını gizlemesi durumunda kullanıldıklarını belirtir. "sınıf X" gibi ileri bildirimler dışında en sık kullanıldıkları yer burası; (burada sadece bir deklarasyon oluşturur). buradaki şablonlar dışında onlardan bahsediyor. ancak, 14.6 / 3 bunların şablonlarda kullanımını listeler.
Johannes Schaub - litb

1

VC için, ileri bildirim ve temel türü belirleme testi:

  1. Aşağıdaki kod tamam derlendi.
    int myint için typedef;
    enum T;
    geçersiz foo (T * tp)
    {
        * tp = (T) 0x12345678;
    }
    enum T: karakter
    {
        bir
    };

Ancak / W4 için uyarı var (/ W3 bu uyarıyı içermiyor)

uyarı C4480: standart olmayan uzatma kullanıldı: enum 'T' için altta yatan türü belirtme

  1. VC (Microsoft (R) 32-bit C / C ++ En İyileştirici Derleyici Sürümü 80x86 için 15.00.30729.01) yukarıdaki durumda buggy görünüyor:

    • enum T'yi gördüğünüzde; VC, enum T türü varsayılan 4 bayt int'i altta yatan tür olarak kullandığını varsayar, bu nedenle oluşturulan derleme kodu:
    ? foo @ YAXPAW4T @@@ Z PROC; foo
    ; Dosya e: \ work \ c_cpp \ cpp_snippet.cpp
    ; Çizgi 13
        itmek
        mov ebp, esp
    ; Satır 14
        mov eax, DWORD PTR _tp $ [ebp]
        mov DWORD PTR [eax], 305419896; 12345678H
    ; Çizgi 15
        pop ebp
        ret 0
    foo @@ YAXPAW4T @@@ Z ENDP; foo

Yukarıdaki montaj kodu /Fatest.asm dosyasından doğrudan çıkar, kişisel tahminim değil. Mov DWORD PTR [eax], 305419896; 12345678H hattı?

aşağıdaki kod pasajı bunu kanıtlar:

    int main (int argc, char * argv)
    {
        Birlik {
            char ca [4];
            Tt;
        } A;
        a.ca [0] = a.ca [1] = a. [ca [2] = a.ca [3] = 1;
        foo (& a.t);
        printf ("% # x,% # x,% # x,% # x \ n", a.ca [0], a.ca [1], a.ca [2], a.ca [3]) ;
        dönüş 0;
    }

sonuç: 0x78, 0x56, 0x34, 0x12

  • enum T'nin ileri bildirimini kaldırın ve enum T'nin tanımından sonra işlev foo tanımını taşıyın: sonuç TAMAM:

Yukarıdaki anahtar talimat:

mov BYTE PTR [eax], 120; 00000078H

nihai sonuç: 0x78, 0x1, 0x1, 0x1

Değerin üzerine yazılmadığını unutmayın

Bu nedenle, VC'de enum ileri bildiriminin kullanılması zararlı olarak kabul edilir.

BTW, sürpriz olmamak üzere, altta yatan türün bildirimi için sözdizimi C # ile aynıdır. Uygulamada, bellek sınırlı olan gömülü sistemle konuşurken temel türü char olarak belirterek 3 bayt kaydetmeye değer buldum.


1

Projelerimde, eski ve üçüncü taraf bileşenlerden gelenlerle başa çıkmak için Namespace-Bound Numaralandırma tekniğini kullandım enum. İşte bir örnek:

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

foo.hBaşlık hakkında hiçbir şey bilmek zorunda olmadığını unutmayın legacy::evil. Yalnızca eski türü legacy::evil(burada: main.cc) kullanan dosyaların dahil edilmesi gerekir enum.h.


0

Sorununuza benim çözümüm:

1 - numaralandırmalar yerine int kullanın: Gelenlerinizi CPP dosyanızdaki (başlıkta değil) anonim bir ad alanında bildirin:

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

Yöntemleriniz özel olduğundan, hiç kimse verileri karıştırmaz. Birisinin size geçersiz veri gönderip göndermediğini test etmek için daha da ileri gidebilirsiniz:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2: Java'da yapıldığı gibi sınırlı sabit örnekleriyle tam bir sınıf oluşturun. İleri ilet, sınıfı bildirir ve ardından CPP dosyasında tanımlar ve yalnızca numaralandırma benzeri değerleri örneklendirir. C ++ böyle bir şey yaptım ve sonuç bir enum (kopya inşaat, operatör =, vb.) Taklit etmek için bazı kod gerekli olduğu gibi, istenen şekilde tatmin edici değildi.

3: Daha önce önerildiği gibi, özel olarak beyan edilen numaralandırmayı kullanın. Bir kullanıcının tam tanımını görmesine rağmen, onu kullanamaz veya özel yöntemleri kullanamaz. Bu nedenle, sınıfınızı kullanarak kodun yeniden derlenmesine gerek kalmadan genellikle mevcut yöntemlerin sıralamasını ve içeriğini değiştirebilirsiniz.

Tahminim çözüm 3 ya da 1 olacaktır.


-1

Enum değişen boyutta bir integral boyutu olabileceğinden (derleyici belirli bir enumun hangi boyuta sahip olduğuna karar verir), enumun işaretçisi de farklı bir boyuta sahip olabilir, çünkü integral bir tiptir (karakterlerin bazı platformlarda farklı boyutta işaretçileri vardır) Örneğin).

Böylece derleyici, numarayı ve kullanıcıyı ona bir işaretçi ileri sürmenize bile izin veremez, çünkü orada bile numaralandırmanın boyutuna ihtiyaç duyar.


-1

Tür öğelerinin olası değerlerini sınırlı bir kümeyle sınırlamak için bir numaralandırma tanımlarsınız. Bu kısıtlama derleme zamanında uygulanacaktır.

İleride daha sonra 'sınırlı bir küme' kullanacağınızı bildirirken herhangi bir değer katmaz: sonraki kodun bundan yararlanabilmesi için olası değerleri bilmesi gerekir.

Derleyici rağmen edilmektedir sabit tür boyutu hakkında endişe, niyet ileri ilan zaman numaralandırma kaybolur.


1
Hayır, sonraki kodun bunun için yararlı olması gereken değerleri bilmesine gerek yoktur - özellikle, sonraki kod yalnızca enum parametrelerini alan veya döndüren bir işlev prototipiyse, türün boyutu önemli değildir. Burada ileri bildirimi kullanarak derleme bağımlılıkları kaldırılabilir.
j_random_hacker

Haklısın. Amaç değerlere değil tipe uymaktır. 0x Enum türleri ile çözüldü.
xtofl
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.