ISO C99 / C11'de, birleşim temelli tür işleme yasaldır, bu nedenle işaretçileri dizi olmayanlara dizinlemek yerine bunu kullanabilirsiniz (çeşitli diğer yanıtlara bakın).
ISO C ++, sendika tabanlı tür karıştırmaya izin vermez. GNU C ++, bir uzantı olarak bunu yapar ve genel olarak GNU uzantılarını desteklemeyen bazı diğer derleyicilerin birleşim tipi araştırmayı desteklediğini düşünüyorum. Ancak bu, kesinlikle taşınabilir kod yazmanıza yardımcı olmaz.
Gcc ve clang'ın mevcut sürümlerinde, switch(idx)bir üye seçmek için a kullanarak bir C ++ üye işlevi yazmak , derleme zamanı sabit dizinlerini optimize edecek, ancak çalışma zamanı dizinleri için korkunç dallı asm üretecektir. Bunun doğal olarak yanlış bir tarafı yok switch(); bu sadece mevcut derleyicilerde gözden kaçan bir optimizasyon hatasıdır. Slava'nın switch () işlevini verimli bir şekilde derleyebilirler.
Bunun çözümü / geçici çözümü, bunu başka şekilde yapmaktır: sınıfınıza / yapınıza bir dizi üyesi verin ve belirli öğelere adlar eklemek için erişimci işlevleri yazın.
struct array_data
{
int arr[3];
int &operator[]( unsigned idx ) {
// assert(idx <= 2);
//idx = (idx > 2) ? 2 : idx;
return arr[idx];
}
int &a(){ return arr[0]; } // TODO: const versions
int &b(){ return arr[1]; }
int &c(){ return arr[2]; }
};
Godbolt derleyici kaşifinde farklı kullanım durumları için asm çıktısına bir göz atabiliriz . Bunlar eksiksiz x86-64 System V işlevleridir, sondaki RET talimatı satır içi olduklarında ne elde edeceğinizi daha iyi göstermek için atlanmıştır. ARM / MIPS / benzer ne olursa olsun.
# asm from g++6.2 -O3
int getb(array_data &d) { return d.b(); }
mov eax, DWORD PTR [rdi+4]
void setc(array_data &d, int val) { d.c() = val; }
mov DWORD PTR [rdi+8], esi
int getidx(array_data &d, int idx) { return d[idx]; }
mov esi, esi # zero-extend to 64-bit
mov eax, DWORD PTR [rdi+rsi*4]
Karşılaştırmak gerekirse, @ Slava'nın switch()C ++ için kullandığı yanıtı , çalışma zamanı değişken dizini için asm'yi böyle yapar. (Önceki Godbolt bağlantısındaki kod).
int cpp(data *d, int idx) {
return (*d)[idx];
}
# gcc6.2 -O3, using `default: __builtin_unreachable()` to promise the compiler that idx=0..2,
# avoiding an extra cmov for idx=min(idx,2), or an extra branch to a throw, or whatever
cmp esi, 1
je .L6
cmp esi, 2
je .L7
mov eax, DWORD PTR [rdi]
ret
.L6:
mov eax, DWORD PTR [rdi+4]
ret
.L7:
mov eax, DWORD PTR [rdi+8]
ret
Bu, C (veya GNU C ++) sendika tabanlı tür punning sürümüne kıyasla açıkça korkunç:
c(type_t*, int):
movsx rsi, esi # sign-extend this time, since I didn't change idx to unsigned here
mov eax, DWORD PTR [rdi+rsi*4]