"Gizli özellikler" soruları söz konusu olduğunda C ++ sevgisi yok mu? Onu oraya atacağımı düşündüm. C ++ 'ın gizli özelliklerinden bazıları nelerdir?
"Gizli özellikler" soruları söz konusu olduğunda C ++ sevgisi yok mu? Onu oraya atacağımı düşündüm. C ++ 'ın gizli özelliklerinden bazıları nelerdir?
Yanıtlar:
Çoğu C ++ programcısı, üçlü operatöre aşinadır:
x = (y < 0) ? 10 : 20;
Ancak, bunun bir değer olarak kullanılabileceğinin farkında değiller:
(a == 0 ? a : b) = 1;
hangisinin kısaltması
if (a == 0)
a = 1;
else
b = 1;
Dikkatle kullanın :-)
(value ? function1 : function2)()
.
function1
ve function2
dolaylı olarak işlev işaretçilerine dönüştürülürse ve sonuç örtük olarak geri dönüştürülürse her yerde çalışmalıdır .
URI'leri C ++ kaynağına hatasız olarak yerleştirebilirsiniz. Örneğin:
void foo() {
http://stackoverflow.com/
int bar = 4;
...
}
goto
C ++ 'ın sahip olduğu kullanım için). İki eğik çizgiyi takip eden her şey bir yorumdur. Bu nedenle, with http://stackoverflow.com
, http
bir etikettir (teorik olarak yazabilirsiniz goto http;
) ve //stackoverflow.com
sadece bir satır sonu yorumudur. Bunların her ikisi de yasal C ++ 'dır, bu nedenle yapı derlenir. Elbette belirsiz bir şekilde yararlı hiçbir şey yapmaz.
goto http;
aslında URL'yi takip etmiyor. :(
İşaretçi aritmetiği.
C ++ programcıları, ortaya çıkabilecek hatalar nedeniyle işaretçilerden kaçınmayı tercih eder.
Yine de gördüğüm en havalı C ++? Analog değişmez değerler.
Buradaki çoğu gönderiye katılıyorum: C ++ çok paradigmalı bir dildir, bu nedenle bulacağınız "gizli" özellikler (ne pahasına olursa olsun kaçınmanız gereken "tanımlanmamış davranışlar" dışında), tesislerin akıllıca kullanımlarıdır.
Bu tesislerin çoğu dilin yerleşik özellikleri değil, kütüphane tabanlı özelliklerdir.
En önemlisi, C dünyasından gelen C ++ geliştiricileri tarafından yıllarca görmezden gelinen RAII'dir . Operatör aşırı yükleme , genellikle hem dizi benzeri davranışı (alt simge operatörü), işaretçi benzeri işlemleri (akıllı işaretçiler) hem de yerleşik benzeri işlemleri (matrisleri çarpma) etkinleştiren yanlış anlaşılan bir özelliktir.
Kullanımı haricinde genellikle zordur, ama bazı çalışma yoluyla gerçekten sağlam kod üretebilir istisna güvenliği başarısız olmayacak olan veya bir taahhüt benzeri olduğunu başaracaktır olduğu özellikler, ya da Geri döndürme arka olacak kodu dahil özelliklerine ( orijinal durumu).
C ++ 'ın en ünlü "gizli" özelliği şablon meta programlamadır , çünkü programınızın çalışma zamanı yerine derleme zamanında kısmen (veya tamamen) çalıştırılmasını sağlar. Yine de bu zordur ve denemeden önce şablonları sağlam bir şekilde kavramanız gerekir.
Diğerleri, C ++ 'ın atası olan C dışında "programlama yolları" üretmek için çoklu paradigmayı kullanır.
Functors kullanarak , ek tür güvenliği ve durum bilgisi olan işlevleri simüle edebilirsiniz. Komut modelini kullanarak kod yürütmeyi geciktirebilirsiniz. Diğer tasarım modellerinin çoğu , "resmi C ++ paradigmaları" listesinde olmaması gereken alternatif kodlama stilleri üretmek için C ++ 'da kolayca ve verimli bir şekilde uygulanabilir.
Şablonları kullanarak , ilk başta düşündüğünüz değil, çoğu türde çalışacak kod üretebilirsiniz. Tip güvenliğini de artırabilirsiniz (otomatik tip güvenli malloc / realloc / ücretsiz gibi). C ++ nesne özellikleri gerçekten güçlüdür (ve bu nedenle dikkatsizce kullanılırsa tehlikelidir), ancak dinamik polimorfizmin bile statik sürümü C ++ ' dadır : CRTP .
Scott Meyers'den " Etkili C ++ " tipi kitapların veya Herb Sutter'ın " Olağanüstü C ++ " tipi kitapların hem okunması kolay hem de C ++ 'nın bilinen ve daha az bilinen özellikleri hakkında bilgi hazineleri olduğunu buldum .
Benim tercih ettiğim şey, herhangi bir Java programcısının saçını dehşetten yükseltmesi gereken bir şey: C ++ 'da, bir nesneye bir özellik eklemenin en nesneye yönelik yolu, üye yerine üye olmayan arkadaş olmayan bir işlevdir. işlev (yani sınıf yöntemi), çünkü:
C ++ 'da, bir sınıf' arabirimi hem üye işlevleri hem de aynı ad alanındaki üye olmayan işlevlerdir.
arkadaş olmayan üye olmayan işlevlerin dahili sınıfa ayrıcalıklı erişimi yoktur. Bu nedenle, üye olmayan arkadaş olmayan bir işlev yerine üye işlevi kullanmak, sınıfın kapsüllenmesini zayıflatacaktır.
Bu, deneyimli geliştiricileri bile şaşırtmaz.
(Kaynak: Diğerlerinin yanı sıra, Herb Sutter'ın 84 Haftanın çevrimiçi Gurusu: http://www.gotw.ca/gotw/084.htm )
Okulda geçirdiğim süre boyunca bunu hiç duymadığım için biraz gizli olduğunu düşündüğüm bir dil özelliği, ad alanı takma adıdır. Takviye belgelerinde bunun örneklerine rastlayana kadar dikkatimi çekmedi. Elbette, artık bunu bildiğime göre, herhangi bir standart C ++ referansında bulabilirsiniz.
namespace fs = boost::filesystem;
fs::path myPath( strPath, fs::native );
using
.
Değişkenler yalnızca bir for
döngünün başlangıç kısmında değil, aynı zamanda sınıflar ve işlevler de bildirilebilir.
for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
...
}
Bu, farklı türlerde birden çok değişkene izin verir.
Dizi operatörü ilişkiseldir.
A [8], * (A + 8) ile eşanlamlıdır. Ekleme ilişkisel olduğundan, bu ..... 8 [A] ile eşanlamlı olan * (8 + A) olarak yeniden yazılabilir.
Yararlı demedin ... :-)
A
hiç önemli değil. Örneğin, A
bir vardı char*
, kod hala geçerli olacaktır.
Az bilinen bir şey, sendikaların da şablon olabileceğidir:
template<typename From, typename To>
union union_cast {
From from;
To to;
union_cast(From from)
:from(from) { }
To getTo() const { return to; }
};
Yapıcılara ve üye işlevlerine de sahip olabilirler. Kalıtımla ilgisi olan hiçbir şey (sanal işlevler dahil).
From
ve To
buna göre ayarlanır ve kullanılır. Böyle bir birleşim, tanımlanmış davranışla da kullanılabilir To
(işaretsiz bir karakter dizisi veya bir başlangıç dizisini paylaşan bir yapı ile From
). Tanımlanmamış bir şekilde kullansanız bile, yine de düşük seviyeli işler için faydalı olabilir. Her neyse, bu bir birleşim şablonunun sadece bir örneğidir - şablonlu bir birleşmenin başka kullanımları olabilir.
C ++ bir standarttır, hiçbir gizli özellik olmamalıdır ...
C ++ çok paradigmalı bir dildir, oradaki son paranızın gizli özellikler olduğuna bahse girebilirsiniz. Pek çok örnekten biri: şablon meta programlaması . Standartlar komitesindeki hiç kimse, derleme zamanında çalıştırılacak bir Turing-complete alt dili olmasını amaçlamadı.
C'de çalışmayan diğer bir gizli özellik, tekli +
operatörün işlevselliğidir . Her türlü şeyi teşvik etmek ve çürütmek için kullanabilirsiniz
+AnEnumeratorValue
Ve daha önce numaralandırma türüne sahip olan numaralandırıcı değeriniz artık değerine uyabilecek mükemmel tamsayı türüne sahiptir. Manuel olarak, bu türü neredeyse hiç tanımazsınız! Bu, örneğin numaralandırmanız için aşırı yüklenmiş bir operatör uygulamak istediğinizde gereklidir.
Sınıf dışı bir tanım olmadan sınıf içi statik başlatıcı kullanan bir sınıf kullanmanız gerekir, ancak bazen bağlanamaz? Operatör, türüne ilişkin varsayımlar veya bağımlılıklar yapmadan bir geçici oluşturmaya yardımcı olabilir
struct Foo {
static int const value = 42;
};
// This does something interesting...
template<typename T>
void f(T const&);
int main() {
// fails to link - tries to get the address of "Foo::value"!
f(Foo::value);
// works - pass a temporary value
f(+Foo::value);
}
Bir işleve iki işaretçi iletmek istiyor ama işe yaramıyor mu? Operatör yardımcı olabilir
// This does something interesting...
template<typename T>
void f(T const& a, T const& b);
int main() {
int a[2];
int b[3];
f(a, b); // won't work! different values for "T"!
f(+a, +b); // works! T is "int*" both time
}
Const referanslara bağlı geçicilerin ömrü, çok az kişinin bildiği bir şeydir. Ya da en azından çoğu insanın bilmediği en sevdiğim C ++ bilgisi parçası.
const MyClass& x = MyClass(); // temporary exists as long as x is in scope
Sık kullanılmayan güzel bir özellik, işlev çapında deneme-yakalama bloğudur:
int Function()
try
{
// do something here
return 42;
}
catch(...)
{
return -1;
}
Ana kullanım, istisnayı diğer istisna sınıfına çevirmek ve yeniden atmak veya istisnalar ve dönüş tabanlı hata kodu işlemeyi arasında çevirmek olacaktır.
return
Function Try bloğunu yakalayabileceğinizi sanmıyorum , sadece yeniden atabilirsiniz.
Birçoğu biliyor identity
/ id
meta işlevini bilir, ancak şablon olmayan durumlarda bunun için güzel bir kullanım durumu vardır: Bildirimleri yazma kolaylığı:
// void (*f)(); // same
id<void()>::type *f;
// void (*f(void(*p)()))(int); // same
id<void(int)>::type *f(id<void()>::type *p);
// int (*p)[2] = new int[10][2]; // same
id<int[2]>::type *p = new int[10][2];
// void (C::*p)(int) = 0; // same
id<void(int)>::type C::*p = 0;
C ++ bildirimlerinin şifresinin çözülmesine büyük ölçüde yardımcı olur!
// boost::identity is pretty much the same
template<typename T>
struct id { typedef T type; };
template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T;
-> pointer<function<void,int>> f(pointer<function<void,void>>);
veya pointer<void(int)> f(pointer<void()>);
veyafunction<pointer<function<void,int>>,pointer<function<void,void>>> f;
Oldukça gizli bir özellik, değişkenleri bir if koşulu içinde tanımlayabilmeniz ve kapsamının yalnızca if ve else bloklarını kapsayabilmesidir:
if(int * p = getPointer()) {
// do something
}
Bazı makrolar, örneğin aşağıdaki gibi bazı "kilitli" kapsamlar sağlamak için bunu kullanır:
struct MutexLocker {
MutexLocker(Mutex&);
~MutexLocker();
operator bool() const { return false; }
private:
Mutex &m;
};
#define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else
void someCriticalPath() {
locked(myLocker) { /* ... */ }
}
Ayrıca BOOST_FOREACH bunu başlık altında kullanır. Bunu tamamlamak için, yalnızca eğer bir durumda değil, aynı zamanda bir anahtarla da mümkündür:
switch(int value = getIt()) {
// ...
}
ve bir süre döngüsünde:
while(SomeThing t = getSomeThing()) {
// ...
}
(ve ayrıca bir for durumunda). Ama bunların hepsinin bu kadar yararlı olup olmadığından pek emin değilim :)
if((a = f()) == b) ...
, ancak bu cevap aslında koşulda bir değişken ilan ediyor.
for(...; int i = foo(); ) ...;
olduğu sürece gövdeden geçecek ve i
her seferinde yeniden başlatacak gibi görünüyor . Gösterdiğiniz döngü basitçe bir değişken bildirimini gösteriyor, ancak aynı anda bir koşul olarak hareket eden bir değişken bildirimi değil :)
Bazen virgül operatörünü geçerli bir şekilde kullanırsınız, ancak kullanıcı tanımlı virgül operatörünün araya girmediğinden emin olmak istersiniz, çünkü örneğin sol ve sağ taraf arasındaki sıra noktalarına güvenirsiniz veya hiçbir şeyin istenen şeyi engellemediğinden emin olmak istersiniz aksiyon. İşte burada void()
devreye giriyor:
for(T i, j; can_continue(i, j); ++i, void(), ++j)
do_code(i, j);
Koşul ve kod için koyduğum yer tutucuları görmezden gelin. Önemli void()
olan, derleyiciyi yerleşik virgül operatörünü kullanmaya zorlayan şeydir. Bu, bazen özellik sınıflarını uygularken de yararlı olabilir.
Yapıcıda dizi başlatma. Örneğin bir sınıfta aşağıdaki gibi bir dizimiz varsa int
:
class clName
{
clName();
int a[10];
};
Yapıcıdaki dizideki tüm öğeleri varsayılan değerine (burada dizinin tüm öğeleri sıfıra) şu şekilde başlatabiliriz:
clName::clName() : a()
{
}
Oooh, bunun yerine evcil hayvan nefretlerinin bir listesini verebilirim:
Artı tarafta
Herhangi bir sınıfın korumalı verilerine ve işlev üyelerine, tanımlanmamış davranışlar olmadan ve beklenen anlamlarla erişebilirsiniz. Nasıl olduğunu görmek için okumaya devam edin. Hata raporunu da okuyun ilgili .
Normalde, C ++ bir sınıfın nesnesinin statik korumalı olmayan üyelerine, bu sınıf sizin temel sınıfınız olsa bile erişmenizi yasaklar.
struct A {
protected:
int a;
};
struct B : A {
// error: can't access protected member
static int get(A &x) { return x.a; }
};
struct C : A { };
Bu yasak: Siz ve derleyici referansın gerçekte neye işaret ettiğini bilmiyorsunuz. Bir C
nesne olabilir , bu durumda sınıfın B
hiçbir işi ve verileri hakkında ipucu yoktur. Bu tür bir erişim, yalnızca x
türetilmiş bir sınıfa veya ondan türetilmiş bir sınıfa referans ise verilir . Ayrıca, üyeleri okuyan bir "atma" sınıfı oluşturarak, rastgele kod parçasının herhangi bir korumalı üyeyi okumasına izin verebilir, örneğin std::stack
:
void f(std::stack<int> &s) {
// now, let's decide to mess with that stack!
struct pillager : std::stack<int> {
static std::deque<int> &get(std::stack<int> &s) {
// error: stack<int>::c is protected
return s.c;
}
};
// haha, now let's inspect the stack's middle elements!
std::deque<int> &d = pillager::get(s);
}
Elbette, gördüğünüz gibi bu çok fazla hasara neden olur. Ancak şimdi, üye işaretçiler bu korumayı atlatmaya izin veriyor! Buradaki kilit nokta, üye göstericinin türünün , adresi alırken belirttiğiniz sınıfa değil , söz konusu üyeyi gerçekten içeren sınıfa bağlı olmasıdır . Bu, kontrolü atlatmamızı sağlar
struct A {
protected:
int a;
};
struct B : A {
// valid: *can* access protected member
static int get(A &x) { return x.*(&B::a); }
};
struct C : A { };
Ve tabii ki std::stack
örnekle de işe yarıyor .
void f(std::stack<int> &s) {
// now, let's decide to mess with that stack!
struct pillager : std::stack<int> {
static std::deque<int> &get(std::stack<int> &s) {
return s.*(pillager::c);
}
};
// haha, now let's inspect the stack's middle elements!
std::deque<int> &d = pillager::get(s);
}
Bu, türetilmiş sınıftaki, üye adını genel yapan ve temel sınıfın üyesine gönderme yapan bir kullanım bildirimi ile daha da kolay olacaktır.
void f(std::stack<int> &s) {
// now, let's decide to mess with that stack!
struct pillager : std::stack<int> {
using std::stack<int>::c;
};
// haha, now let's inspect the stack's middle elements!
std::deque<int> &d = s.*(&pillager::c);
}
Diğer bir gizli özellik, işlev işaretçilerine veya referanslara dönüştürülebilen sınıf nesnelerini çağırabilmenizdir. Aşırı yük çözümü, bunların sonucunda yapılır ve argümanlar mükemmel bir şekilde iletilir.
template<typename Func1, typename Func2>
class callable {
Func1 *m_f1;
Func2 *m_f2;
public:
callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { }
operator Func1*() { return m_f1; }
operator Func2*() { return m_f2; }
};
void foo(int i) { std::cout << "foo: " << i << std::endl; }
void bar(long il) { std::cout << "bar: " << il << std::endl; }
int main() {
callable<void(int), void(long)> c(foo, bar);
c(42); // calls foo
c(42L); // calls bar
}
Bunlara "vekil çağrı işlevleri" denir.
Gizli özellikler:
Bir işlev, istisna belirtimlerinde listelenmeyen bir istisna atarsa, ancak işlevin std::bad_exception
istisna belirtiminde varsa, istisna std::bad_exception
otomatik olarak dönüştürülür ve atılır. Bu şekilde en azından bad_exception
a'nın atıldığını bileceksiniz . Daha fazlasını buradan okuyun .
fonksiyon deneme blokları
Bir sınıf şablonunda açıklayıcı typedef'lerdeki şablon anahtar sözcüğü. Üye şablon uzmanlık adı sonra görünürse .
, ->
veya ::
operatör ve bu ismi açıkça nitelikli şablon parametreleri, anahtar kelime şablonla önek üye şablon adı vardır. Daha fazlasını buradan okuyun .
fonksiyon parametre varsayılanları çalışma zamanında değiştirilebilir. Daha fazlasını buradan okuyun .
A[i]
kadar iyi çalışıyor i[A]
Bir sınıfın geçici örnekleri değiştirilebilir! Sabit olmayan bir üye işlevi geçici bir nesnede çağrılabilir. Örneğin:
struct Bar {
void modify() {}
}
int main (void) {
Bar().modify(); /* non-const function invoked on a temporary. */
}
Daha fazlasını buradan okuyun .
:
Üçlü ( ?:
) operatör ifadesinden önce ve sonra iki farklı tür varsa , sonuçta ortaya çıkan ifade türü, ikisi arasında en genel olanıdır. Örneğin:
void foo (int) {}
void foo (double) {}
struct X {
X (double d = 0.0) {}
};
void foo (X) {}
int main(void) {
int i = 1;
foo(i ? 0 : 0.0); // calls foo(double)
X x;
foo(i ? 0.0 : x); // calls foo(X)
}
map::operator[]
anahtar eksikse girdi oluşturur ve varsayılan olarak oluşturulmuş girdi değerine başvuru döndürür. Böylece yazabilirsiniz:
map<int, string> m;
string& s = m[42]; // no need for map::find()
if (s.empty()) { // assuming we never store empty values in m
s.assign(...);
}
cout << s;
Kaç C ++ programcısının bunu bilmediğine hayret ediyorum.
.find()
.
const map::operator[]
hata mesajları üretir"
Fonksiyonların veya değişkenlerin isimsiz bir ad alanına yerleştirilmesi, static
bunların dosya kapsamıyla sınırlandırılmasının kullanımını ortadan kaldırır.
static
küresel kapsamda hiçbir şekilde eskimiş değil. (Referans için: C ++ 03 §D.2)
static
kullanım yalnızca bir sınıf türü veya işlev içinde kullanılmalıdır.
Sınıf şablonlarında sıradan arkadaş işlevlerinin tanımlanması özel dikkat gerektirir:
template <typename T>
class Creator {
friend void appear() { // a new function ::appear(), but it doesn't
… // exist until Creator is instantiated
}
};
Creator<void> miracle; // ::appear() is created at this point
Creator<double> oops; // ERROR: ::appear() is created a second time!
Bu örnekte, iki farklı örnekleme iki özdeş tanım oluşturur - ODR'nin doğrudan ihlali
Bu nedenle, sınıf şablonunun şablon parametrelerinin o şablonda tanımlanan herhangi bir arkadaş işlevi türünde göründüğünden emin olmalıyız (belirli bir dosyada bir sınıf şablonunun birden fazla somutlaştırılmasını önlemek istemiyorsak, ancak bu pek olası değildir). Bunu önceki örneğimizin bir varyasyonuna uygulayalım:
template <typename T>
class Creator {
friend void feed(Creator<T>*){ // every T generates a different
… // function ::feed()
}
};
Creator<void> one; // generates ::feed(Creator<void>*)
Creator<double> two; // generates ::feed(Creator<double>*)
Sorumluluk Reddi: Bu bölümü C ++ Şablonlarından yapıştırdım : Tam Kılavuz / Bölüm 8.4
Az biliniyor, ancak aşağıdaki kod yeterli
void f() { }
void g() { return f(); }
Aşağıdaki tuhaf görünümlü biri kadar
void f() { return (void)"i'm discarded"; }
Bunu bilerek, bazı alanlarda yararlanabilirsiniz. Bir örnek: void
işlevler bir değer döndüremez, ancak siz de hiçbir şey döndüremezsiniz, çünkü bunlar void olmayan şekilde başlatılabilir. Değeri yerel bir değişkene depolamak yerine, bu bir hataya neden olur void
, doğrudan bir değer döndürmeniz yeterlidir.
template<typename T>
struct sample {
// assume f<T> may return void
T dosomething() { return f<T>(); }
// better than T t = f<T>(); /* ... */ return t; !
};
Bir dosyayı dizelerden oluşan bir vektör olarak okuyun:
vector<string> V;
copy(istream_iterator<string>(cin), istream_iterator<string>(),
back_inserter(V));
vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());
- ikinci
Bit alanlarını şablonlayabilirsiniz.
template <size_t X, size_t Y>
struct bitfield
{
char left : X;
char right : Y;
};
Henüz bunun için herhangi bir amaç bulamadım, ama kesinlikle beni şaşırttı.
Herhangi bir programlama dilinin en ilginç gramerlerinden biri.
Bunlardan üçü birbirine ait ve ikisi tamamen farklı bir şey ...
SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));
Üçüncü ve beşinci hariç tümü SomeType
yığın üzerinde bir nesneyi tanımlar ve onu başlatır ( u
ilk iki durumda ve varsayılan kurucu dördüncü durumdadır. Üçüncüsü, parametre almayan ve a döndüren bir işlev bildirmektir SomeType
. Beşincisi de benzer şekilde bildirir SomeType
adlandırılmış türün değerine göre bir parametre alan bir işlev u
.
İleriye dönük beyanlardan kurtulmak:
struct global
{
void main()
{
a = 1;
b();
}
int a;
void b(){}
}
singleton;
?: Operatörler ile anahtar ifadeleri yazma:
string result =
a==0 ? "zero" :
a==1 ? "one" :
a==2 ? "two" :
0;
Her şeyi tek bir satırda yapmak:
void a();
int b();
float c = (a(),b(),1.0f);
Memset olmadan yapıları sıfırlama:
FStruct s = {0};
Açı ve zaman değerlerini normalleştirme / sarma:
int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150
Referans atama:
struct ref
{
int& r;
ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;
FStruct s = {};
daha da kısadır.
main
? Ben öneririm global().main();
(ve sadece tekil unutun size 's ömrünü aldığı, geçici sadece iş genişletilmiş olabilir )
Üçlü koşullu operatör ?:
, ikinci ve üçüncü işleneninin "uygun" tiplere sahip olmasını gerektirir (gayri resmi olarak konuşur). Ancak bu gereksinimin bir istisnası vardır (punto amaçlanmıştır): ya ikinci ya da üçüncü işlenen void
, diğer işlenenin türüne bakılmaksızın bir atma ifadesi (türü olan ) olabilir.
Başka bir deyişle, ?:
operatör kullanılarak aşağıdaki tam olarak geçerli C ++ ifadeleri yazılabilir.
i = a > b ? a : throw something();
BTW, atma ifadesinin aslında bir ifade (tipte void
) olduğu ve bir ifade olmadığı gerçeği , C ++ dilinin az bilinen bir başka özelliğidir. Bu, diğer şeylerin yanı sıra, aşağıdaki kodun tamamen geçerli olduğu anlamına gelir
void foo()
{
return throw something();
}
Bu şekilde yapmanın pek bir anlamı olmasa da (belki bazı genel şablon kodlarında bu kullanışlı olabilir).
Hakimiyet kuralı kullanışlıdır, ancak çok az bilinir. Bir temel sınıf kafesi boyunca benzersiz olmayan bir yolda bile, kısmen gizli bir üye için ad aramanın, üye sanal bir temel sınıfa aitse benzersiz olduğunu söylüyor:
struct A { void f() { } };
struct B : virtual A { void f() { cout << "B!"; } };
struct C : virtual A { };
// name-lookup sees B::f and A::f, but B::f dominates over A::f !
struct D : B, C { void g() { f(); } };
Bunu hizalama desteğini uygulamak için kullandım , hakimiyet kuralı aracılığıyla en katı hizalamayı otomatik olarak çözen hizalama .
Bu sadece sanal işlevler için değil, aynı zamanda typedef adları, statik / sanal olmayan üyeler ve diğer her şey için de geçerlidir. Meta programlarda üzerine yazılabilir özellikler uygulamak için kullanıldığını gördüm.
struct C
...? Şerefe.