it-swarm-tr.com

C ++ ile bir enum bildirme iletme

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

enum E;

void Foo(E e);

enum E {A, B, C};

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

Açıklama 2: Bunu, enum alan bir sınıfta özel yöntemlere sahip olduğum için yapıyorum ve enum değerlerinin açığa çıkmasını istemiyorum - örneğin, kimsenin E olarak tanımlandığını bilmesini istemiyorum.

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

proje olarak X, kullanıcılarımın bilmesini istediğim bir şey değil.

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

Derleyici gelince - bu GCC.

253
szevvy

Enum'un bildirilmemesinin nedeni, değerleri bilmeden, derleyicinin enum değişkeni için gereken depolamayı bilmemesidir. C++ Derleyicilerinin belirtilen tüm değerleri içermesi için gereken boyuta göre gerçek depolama alanını belirtmesine izin verilir. Görünen her şey ileri bildirim ise, çeviri birimi hangi depolama boyutunun seçileceğini bilemez - bir karakter veya int veya başka bir şey olabilir.


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

Bir numaralandırmanın altında türü , numaralandırmada tanımlanan tüm numaralandırıcı değerlerini temsil edebilen bir ayrılmaz türdür. Bir numaralandırıcının değeri int veya unsigned int ile uyuşmadığı sürece, temel türün int değerinden daha büyük olmaması haricinde, bir numaralandırma için temel tür olarak hangi uygulama türünün kullanıldığı tanımlanır. numaralandırıcı listesi boşsa, temel tür, numaralandırma 0 değerine sahip tek bir numaralandırıcıymış gibi olur. sizeof() değeri bir numaralandırma türüne uygulanan bir numaralandırma türünün bir nesnesi veya bir numaralandırıcı, alttaki türe uygulanan sizeof() değeridir.

İşleve arayan , çağrı yığınını doğru şekilde ayarlamak için parametrelerin boyutlarını bilmesi gerektiğinden, numaralandırma listesindeki numaralandırmaların sayısı önceden bilinmelidir. fonksiyon prototipi.

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.

206
KJAWolf

Enums bildirimlerinin bildirimi de C++ 0x ile mümkündür. Önceden, enum türlerinin ileriye dek 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 doğru beyan edilebilir:

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.
188
user119017

Son gelişmelerden sonra buraya güncel bir cevap ekliyorum.

Depolama türünü aynı anda bildirdiğiniz sürece, C++ 11'de bir enum bildirimi yapabilirsiniz. Sözdizimi şöyle görünür:

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

....

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

Aslında, işlev hiçbir zaman numaralandırmanın değerlerine atıfta bulunmuyorsa, bu noktada tam bir bildirime ihtiyacınız yoktur.

Bu, G ++ 4.6 ve sonrası tarafından desteklenmektedir (daha yeni sürümlerde -std=c++0x veya -std=c++11). Visual C++ 2013 bunu destekliyor; önceki sürümlerde henüz çözemediğim bir tür standart dışı desteğe sahipti - basit bir ileri bildirim bildiriminin yasal olduğunu, ancak YMMV'nin bir önerisi olduğunu gördüm.

73
Tom

C++ 'da işleri bildirmek çok faydalıdır çünkü derleme süresini önemli ölçüde hızlandırır . Aşağıdakileri içeren C++ 'da aşağıdakileri bildirebilirsiniz: struct, class, function, vb ...

Fakat C++ 'da bir enum bildirir misiniz?

Hayır yapamazsın.

Ama neden izin vermiyorsun? İzin verildiyse, başlık dosyanızda enum türünüzü ve kaynak dosyanızdaki enum değerlerinizi tanımlayabilirsiniz. Doğru izin verilmesi gerektiği gibi geliyor?

Yanlış.

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

Bu ne anlama geliyor?

Bu, enum 'ın temel türünün, tanımlanmış enum öğesinin tüm değerlerine sahip olana kadar tam olarak belirlenemediği anlamına gelir. Hangisi sizin enum'nizin bildirimini ve tanımını ayıramazsınız. Ve bu nedenle, C++ 'a enum bildirirsiniz.

ISO C++ standardı S7.2.5:

Temel numaralandırma türü, numaralandırmada tanımlanan tüm numaralandırıcı değerlerini temsil edebilecek bir bütündür. Bir numaralandırıcının değeri bir int veya unsigned int ile uyuşmadığı sürece, temel türün int değerinden daha büyük olmaması haricinde, bir numaralandırma için temel tür olarak hangi uygulama türünün kullanıldığı tanımlanır. Numaralandırıcı listesi boşsa, temel tür numaralandırma, 0 değerine sahip tek bir numaralandırıcıymış gibi olur. Bir numaralandırma türüne uygulanan sizeof() değeri, bir numaralandırma türü nesnesi veya bir numaralandırıcı, sizeof() uygulanan değeridir. temel tip için.

Bir numaralandırılmış türün boyutunu C++ 'da sizeof işlecini kullanarak belirleyebilirsiniz. Numaralandırılan türün boyutu, temel türünün boyutudur. Bu şekilde, derleyicinizin enum öğeniz için hangi türü kullandığını tahmin edebilirsiniz.

Peki ya enum türünüzü bu şekilde açıkça belirtirseniz:

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

Daha sonra enum adresinizi bildirir misiniz?

Hayır. Ama neden olmasın?

enum türünü belirtmek aslında geçerli C++ standardının bir parçası değildir. Bu bir VC++ uzantısıdır. C++ 0x olsa bir parçası olacak.

Kaynak

30
Brian R. Bondy

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

İleriye doğru beyan eden ilanlar standart değildir, çünkü farklı enum tiplerine yönelik işaretçilerin aynı boyutta olması garanti edilmez. Derleyicinin bu boyutta hangi boyutta işaretçilerin kullanılabileceğini bilmek için tanımı görmesi gerekebilir.

Uygulamada, en azından bütün popüler derleyicilerde, enums için işaretçiler tutarlı bir boyuttadır. Örneğin, Enums bildirimi, Visual C++ tarafından bir dil uzantısı olarak sağlanmıştır.

13
James Hopkin

Gerçekten de enum bildirgesi diye bir şey yoktur. Bir enum'un tanımı, enum kullanan diğer koda bağlı olabilecek herhangi bir kod içermediğinden, ilk önce beyan ettiğinizde enumu tamamen tanımlamak sorun olmaz.

Enum'un tek kullanımı özel üye işlevleri ise, enumu kendisinin o sınıfın özel bir üyesi yaparak kapsülleme uygulayabilirsiniz. Enum hala, ilan noktasında, yani sınıf tanımı içinde tam olarak tanımlanmalıdır. Bununla birlikte, buradaki özel üye işlevlerini bildirmek kadar büyük bir sorun değildir ve uygulama içindekilerin bundan daha kötü bir şekilde ortaya çıkması değildir.

Uygulama detaylarınız için daha derin bir gizlemeye ihtiyacınız varsa, onu yalnızca saf sanal işlevlerden ve arayüzü tamamen uygulayan (kalıtımsal) somut, tamamen gizli bir sınıftan oluşan soyut bir arabirime bölebilirsiniz. Sınıf örneklerinin oluşturulması, bir fabrika veya arayüzün statik bir üye işlevi tarafından gerçekleştirilebilir. Bu şekilde, gerçek sınıf adı, özel işlevlerini bıraksa bile, açığa çıkmaz.

7

Sadece sebebinin aslında is belirtildiği gibi, enum büyüklüğünün henüz bildirmeden sonra henüz bilinmediğine dikkat çekmek. Pekala, bir işaretçi iletmek veya ileriye dönük yapı tanımının kendisinde belirtilen bir yerden bir nesneye atıfta bulunmak için bir yapı bildirimi kullanırsınız.

Bir enum bildirmek çok faydalı olmaz, çünkü bir enum değerinden geçmek ister. Bir işaretçi bile bulamadınız, çünkü yakın zamanda bazı platformlarda karakter için int ya da uzun zamandır karakterden farklı boyutlarda işaretçiler kullandığını söylemiştim. Bu yüzden hepsi enum içeriğine bağlıdır.

Geçerli C++ Standardı gibi bir şey yapmanın açıkça yasaklanması

enum X;

(7.1.5.3/1 içinde). Ancak bir sonraki yıla göre bir sonraki C++ Standardı aşağıdakileri sağlar; bu da beni gerçekten problemi ikna etti has temel tiple ilgili:

enum X : int;

Bir "opak" enum bildirimi olarak bilinir. Aşağıdaki kodda X değerine göre bile kullanabilirsiniz. Ve numaralandırıcılar daha sonra numaralandırmanın daha sonra yeniden ilanında tanımlanabilir. Mevcut taslakta 7.2 bölümüne bakınız.

Bu şekilde yapardım:

[genel başlıkta]

typedef unsigned long E;

void Foo(E e);

[iç başlıkta]

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

FORCE_32BIT ekleyerek Econtent'in uzun sürdüğünü garanti ediyoruz, bu yüzden E ile değiştirilebilir.

4
Laurie Cheers

GCC’de ilan edilemez!

İlginç tartışma burada

2
prakash

Enum'u bir yapıya sarabilir, bazı yapıcılara ekleyebilir ve dönüşümleri yazabilir ve bunun yerine yapıyı ileriye aktarabilirsiniz.

#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ş gibi görünüyor: http://ideone.com/TYtP2

2
Leszek Swirski

Enum'unuzun başlık dosyanızda görünmesini istemiyorsanız ve yalnızca özel yöntemlerle kullanıldığından emin olursanız, bir çözüm pimpl ilkesine uymak olabilir.

Bu yalnızca şunu bildirerek sınıftaki iç kısımları başlıklarda gizlemeyi sağlayan bir tekniktir:

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

Daha sonra, uygulama dosyanızda (cpp), dahililerin temsili olacak bir sınıf ilan edersiniz.

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

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

Uygulamayı sınıf kurucusunda dinamik olarak oluşturmalı ve yıkıcıda silmelisiniz ve public yöntemini uygularken kullanmanız gerekir:

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

Pimpl kullanmanın artıları vardır, biri sınıf başlığınızı uygulamasından ayırır, bir sınıf uygulamasını değiştirirken diğer sınıfları yeniden derlemeye gerek yoktur. Bir diğeri, derleme zamanınızı hızlandırıyor çünkü başlıklar çok basit.

Ama bu kullanmak çok acı verici bir şey, bu yüzden gerçekten kendinize, enumunuzu başlıktaki özel olarak ilan etmek bu kadar sıkıntı mı diye sormalısınız.

2
Vincent Robert

Projelerimde, eski ve 3. parti bileşenlerden enums ile başa çıkabilmek için Namespace-Bound Enumeration tekniğini kullandım. İş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.h başlığının legacy::evil hakkında hiçbir şey bilmek zorunda olmadığını unutmayın. Yalnızca legacy::evil (burada: main.cc) türünü kullanan dosyaların enum.h içermesi gerekir.

1
mavam

VC için, ileriye dönük bildirim ve altta yatan türü belirtme testi:

  1. aşağıdaki kod tamam olarak derlenmiştir.
 typedef int myint; 
 enum T; 
 geçersiz foo (T * tp) 
 {
 * tp = (T) 0x12345678; 
} 
 enum T: char 
 {
 A 
}; 

Ancak/W4 için uyarı aldım (/ W3 bu uyarıya maruz kalmadı)

uyarı C4480: standart olmayan uzantı: 'T' enum için altta yatan tür belirtildi

  1. VC (Microsoft (R) 32-bit C/C++ 80x86 için Derleyici Sürüm 15.00.30729.01'i Optimize Etme) yukarıdaki durumda görünüyor:

    • enum T görünce; VC, T enum tipinin temel tip olarak varsayılan 4 bayt int kullandığını varsayar, bu nedenle üretilen Assembly kodu şöyledir:
? foo @@ YAXPAW4T @@@ Z PROC; varsayılan değer
    ; E dosyası:\work\c_cpp\cpp_snippet.cpp 
; Satır 13 
 Ebp 
 Mov ebp, esp 
; İtin; Satır 14 
 Mov eax, DWORD PTR _tp $ [ebp] 
 Mov DWORD PTR [eax], 305419896; 12345678H 
; Satır 15 
 Pop ebp 
 Ret 0 
? Foo @@ YAXPAW4T @@@ Z ENDP; varsayılan değer

Yukarıdaki Meclis kodu, benim kişisel tahminime göre doğrudan /Fatest.asm adresinden çıkarıldı. Hareketli DWORD PTR [eax], 305419896'yı görüyor musunuz? 12345678H hattı?

aşağıdaki kod parçacığını kanıtlıyor:

 int main (int argc, char * argv) 
 {
 birliği {
 char ca [4]; 
 T t; 
} 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]); 
 return 0; 
} 

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

  • enum T'nin ileriye yönelik bildirimini kaldırdıktan sonra ve enum T'nin tanımından sonra foo işlevinin tanımını hareket ettirin: sonuç tamam:

yukarıdaki anahtar talimat olur:

mov BYTE PTR [eax], 120; 00000078H

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

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

Bu yüzden, enum bildirgesinin VC 'de kullanılması zararlı olarak kabul edilir.

BTW, sürpriz yapmamak için, temel türün bildirimi için sözdizimi C # ile aynıdır. Uygulamada, bellekte sınırlı olan gömülü sistemle konuşurken altta yatan türü char olarak belirterek 3 bayttan tasarruf etmenin değerinde olduğunu buldum.

1
zhaorufei

Bu çarptığından beri bir çeşit çekişme var (bir çeşit), işte standarttan bazı bitler. Araştırmalar, standardın ileriye dönük beyanı gerçekten tanımlamadığını ve açıkça açıklamanın, ilanların ileriye dönük olarak beyan edilebileceğini veya olamayacağını açıkça ifade etmiyor.

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

Temel numaralandırma türü, numaralandırmada tanımlanan tüm numaralandırıcı değerlerini temsil edebilecek bir bütündür. Bir numaralandırıcının değeri bir int veya imzasız int'ye uymadığı sürece, temel türün int'den büyük olmayacağı dışında, bir numaralandırma için temel tür olarak hangi integral türünün kullanıldığı uygulama tanımlanmıştır. Numaralandırıcı listesi boşsa, temel tür, numaralandırma, 0 değerine sahip tek bir numaralandırıcıya sahipmiş gibidir. sizeof () temel tipe uygulanır.

Bu nedenle, temel bir enum tipi uygulama tarafından tanımlanmıştır, bir küçük kısıtlama ile.

Ardından, ileriye dönük beyanlarda herhangi bir standarda yaklaştığımız kadar yakın olan “eksik tipler” (3.9) bölümüne geçiyoruz:

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

Bir sınıf tipi ("sınıf X" gibi) bir çeviri biriminde bir noktada eksik olabilir ve daha sonra tamamlanabilir; "X sınıfı", her iki noktada da aynı türdedir. Bir dizi nesnesinin bildirilen türü, eksik bir sınıf türü dizisi olabilir ve bu nedenle eksik olabilir; Sınıf tipi daha sonra çeviri biriminde tamamlanırsa, dizi tipi tamamlanır; Bu iki noktadaki dizi tipi aynı tiptir. Bir dizi nesnesinin bildirilen türü bilinmeyen bir boyut dizisi olabilir ve bu nedenle bir çeviri biriminde bir noktada eksik olabilir ve daha sonra tamamlanır; bu iki noktadaki dizi türleri ("T'nin bilinmeyen sınır dizisi" ve "N T dizisi") farklı türlerdir. Bilinmeyen boyutta bir dizinin işaretçisi veya bilinmeyen bir boyut dizisi olarak typedef bildiriminin tanımladığı bir türün türü tamamlanamaz.

Bu yüzden, standart hemen hemen bildirilebilecek türleri ortaya koydu. Enum orada değildi, bu yüzden derleyici yazarları genellikle, dayanak tipinin değişken büyüklüğü nedeniyle standart tarafından izin verilmeyen ilan olarak kabul ederler.

Bu da mantıklı. Enums'lere genellikle değer durumlarda başvurulur ve derleyicinin bu durumlarda depolama boyutunu bilmesi gerekir. Depolama boyutu uygulama tanımlandığından, birçok derleyici, her enumun alt tipi için yalnızca 32 bitlik değerleri kullanmayı seçebilir, bu noktada bunları bildirmek mümkün olur. İlginç bir deney, görsel stüdyosunda bir enum bildirmeyi ve ardından ne olduğunu görmek için yukarıda açıklandığı gibi (int) boyutundan daha büyük bir tür kullanmaya zorlamayı denemek olabilir.

1
Dan Olson

Senin sorunun benim çözümüm ya da olacaktır:

1 - enums yerine int kullanın: Girdilerinizi CPP dosyanızdaki adsız bir ad alanında bildirin (başlıkta değil):

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

Yöntemlerin özel olduğundan, kimse verilere bulaşmaz. Birisi size geçersiz bir veri gönderirse 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 olduğu gibi sınırlı inşaat örnekleri içeren tam bir sınıf oluşturun. İleri sınıf bildirir ve sonra onu CPP dosyasında tanımlayın ve yalnızca enum benzeri değerleri yazın. C++ 'da böyle bir şey yaptım ve sonuç, bir enum simüle etmek için bazı kodlara ihtiyaç duyduğundan (kopya inşaatı, operatör = vb.) Gerektiği kadar tatmin edici değildi.

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

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

0
paercebal