> 'Funciton Overloading / İşlev Yüklemesi' : Özünde aynı olan fonksiyonların isimlerinin de aynı olmasını sağlamaktır. Fakat gövdelerindeki kodlamalarda farklılıklar olabilir. >> Fonksiyon çağrısı ile hangi fonksiyon yüklemesinin ilişkilendirilmesi/bağlanmasına dair iki adet yöntem vardır. Bunlar 'static binding' ve 'dynamic binding'. >>> 'static binding' veya 'early binding' : Derleyicinin, DERLEME ZAMANINDA koda bakarak, işbu fonksiyon çağrısını hangi fonksiyona bağlayacağına karar vermesi durumudur. >>> 'dynamic binding' veya 'late binding' : İşbu fonksiyon çağrısı ile hangi fonksiyon yüklemesinin çağrılacağının ÇALIŞMA ZAMANINDA belirlenmesi durumudur. >> DERLEME ZAMANINDA derleyiciler koda bakarak hangi fonksiyon yüklemesinin çağrılacağına karar veriyorlar. ÇALIŞMA ZAMANINA dair bir maliyeti yoktur. >> Bu mekanizma da kendi içerisinde farklı kural setleri tanımlar. Bunlar " 'function overloading' var mı yok mu?" sorusu ve " Hangi 'function overload' çağrıldı?" sorularını cevaplar. >>> " 'function overloading' var mı yok mu?" sorusuna cevap veren kural setleri: (İş bu fonksiyonların çağrılıp çağrılmamasına bakılmaz. Sadece var mı yok mu sorusu cevaplanır) >>>> Birden fazla aynı isimli fonksiyonlar, aynı kapsamda(scope) bildirilmiş iseler 'function overloading' vardır. Yani isimlerin ait oldukları scope birbirinden farklı ise 'function overloading' yoktur. Olsa olsa 'name masking' durumu vardır. * Örnek 1, // Some code here... int foo(int); // Global namespace scope. // I int main() { int foo(int, int); // Block scope. // II // Burada 'function overloading' yoktur. 'name masking' vardır. // NOT : İSİM ARAMA, İSİM BULUNDUKTAN SONRA TAMAMLANIR VE ASLA DEVAM ETMEZ. foo(12); // SENTAKS HATASI // Yukarıdaki fonksiyon çağrısından sonra derleyici 'foo' ismini aramaya başlar ve 'II' nolu // fonksiyon bildirimini gördükten sonra aramayı bitirir. İş bu fonksiyon çağrısı ile 'II' nolu // fonksiyonu bağlar. Bahsi geçen fonksiyon iki parametre aldığından ve biz tek parametre geçtiğimiz // için de SENTAKS HATASI oluşur. ::foo(15); // LEGAL // Artık derleyici, yukarıdaki fonksiyon ismini 'global namespace' alanında aramaya başlayacaktır. // 'I' nolu fonksiyon bildirimini gördükten sonra aramayı bitirir. İş bu fonksiyon çağrısı ile 'I' // nolu fonksiyonu bağlar. } >>>> Bildirilmiş aynı isimli fonksiyonların imzaları da farklı olacaktır. Burada imzadan kastedilen şey fonksiyonun parametrik yapısının, geri dönüş değerinin türü göz ardı edilerek, ele alınmasıdır. Velevki imzaları da aynı olsaydı bu durum 'function redeclaretion' olacaktı ve hem C hem de C++ dillerinde bu LEGAL. Eğer imzaları aynı fakat geri döndüş değerlerinin türü farklı olsaydı bu durum SENTAKS HATASI olacaktır. * Örnek 1, // Some code here... int foo(int); // Normal bir fonksiyon bildirimi. int foo(const int); // Eğer parametre 'pointer' değilse buradaki 'const' anahtar sözcüğü 'function overload' mekanizmasını // tetiklemez. Dolayısıyla bu kod bildirimi 'function redeclaretion' olur. void foo(int); // Sentaks hatasıdır. Çünkü isimleri ve parametrik yapısı aynı olan fakat geri dönüş değerinin türü // farklı olduğu fonksiyon bildirimleri SENTAKS HATASI oluşturur. void func(int* ptr); // Normal bir fonksiyon bildirimi. void func(int* const ptr); // Burada 'ptr' nin kendisi 'const' olduğundan dolayı burada 'function overloading' söz konusu değildir. // 'function redeclaretion' vardır. void func(const int* ptr); // BURADA ARTIK 'function overloading' VARDIR. // NOT : FONKSİYONLARI BİRDEN FAZLA KEZ BİLDİREBİLİRİZ FAKAT SADECE BİR DEFA TANIMLAYABİLİRİZ. void myFunc(int&); // Normal bir fonksiyon bildirimi. void myFunc(const int& ); // BURADA ARTIK 'function overloading' VARDIR. void myOtherFunc(int x, int y = 10); // Normal bir fonksiyon bildirimi. void myOtherFunc(int x); // BURADA ARTIK 'function overloading' VARDIR. // Buradan da anlayabiliriz ki 'default argument' olup olmaması imzayı değiştirmiyor. İlk fonksiyon // çağrısı aslında iki parametre alan, ikinci çağrı ise tek parametre alan bir fonksiyon. Bundan dolayı // 'function overloading' mevcut. void theFunc(int x, int y = 10); // Normal bir fonksiyon bildirimi. void theFunc(int x, int y); // Burada artık 'function redeclaretion' söz konusudur. // Tür eş ismi kullanılması, 'function overloading' mekanizmasını tetiklemez. typedef int MyInt; // using MyInt = int; void theFuncTwo(int); // Normal bir fonksiyon bildirimi. void theFuncTwo(MyInt); // Burada artık 'function redeclaretion' söz konusudur. // Gerek C dilinde gerek C++ dilinde üç ayrı 'char' türü olduğundan, aşağıdaki kullanım // 'function overloading' mekanizmasını tetikler. void theFuncThree(char); // Normal bir fonksiyon bildirimi. void theFuncThree(signed char); // BURADA ARTIK 'function overloading' VARDIR. void theFuncThree(unsigned char); // BURADA ARTIK 'function overloading' VARDIR. // Her ne kadar derleyiciler 'enum' türleri arka planda 'int' türler olarak ele alsa da aslında bu iki // tür birbirinden farklıdır. Yani 'function overloading' mekanizmasını tetiklerler. Farklı 'enum' // türleri de birbirinden farklıdır. enum Color{ White, Gray }; void theFuncFour(int); // Normal bir fonksiyon bildirimi. void theFuncFour(Color); // BURADA ARTIK 'function overloading' VARDIR. // AŞAĞIDAKİ KULLANIMI DİKKATLİ İNCELEYİNİZ #include void theFuncFive(int32_t); void theFuncFive(int); // Eğer 'int32_t' tür eş ismine karşılık gelen tür 'int' ise yukarıdaki senaryo 'function redeclaretion', // başka bir türe karşılık ise, örneğin 'long', 'function overloading' vardır. Bu durumda, yukarıdaki // kullanım DERLEYİCİYE BAĞLIDIR. void theFuncSix(int (*p)[10]); void theFuncSix(int (*p)[11]); void theFuncSix(int (*p)[12]); // Burada üç farklı 'overload' vardır çünkü her bir fonksiyonun parametresi, o elemanlı bir diziyi // gösteren gösterici türündendir. Burada diziyi gösteren gösterici mevzu bahistir. void theFuncSeven(int(int)); void theFuncSeven(int (*)(int)); // Nasılki "int*" ve "int []" tip parametreler 'function redeclaretion' olarak geçiyorsa, bu da aynı // şekilde 'function redeclaretion' olarak geçiyor. >>> " Hangi 'function overload' çağrıldı?" sorusuna, ki aynı zamanda bu soru 'function overload resolution' ile ilgilidir, cevap veren kural setleri: >>>> 'function overloading' söz konusudur fakat ilgili fonksiyon çağrısı ile işbu fonksiyonların yüklemeleri birbirlerine bağlanamayabilir. Yani 5 adet fonksiyon yüklemesi vardır fakat çeşitli nedenlerden dolayı çağıran kod bunların hiç birine bağlanamaz. Bu durumda da iki farklı SENTAKS HATASI oluşur. Bunlardan bir tanesine 'no-match' tipi, diğerine 'ambiguity' denir. >>>>> 'no-match' şeklindeki sentaks hatalarında, fonksiyon çağrısında kullanılan parametreler ile fonksiyon bildirimlerindeki parametrelerin eşleşmemesi durumudur. * Örnek 1, void foo(int); void foo(double); void foo(long); int main(){ int x = 5; foo(&x); // Artık burada 'no-match' oluşacaktır çünkü 'int*' türünden yukarıdaki 'foo' fonksiyonlarının // parametrelerine dönüşüm mevcut DEĞİL. FAKAT AYNI ZAMANDA ÜÇ ADET DE 'function overload' // SÖZ KONUSUDUR. } >>>>> 'ambiguity' şeklindeki sentaks hatalarında ise derleyici 'function overload resolution' işlemi sonucunda iki farklı fonksiyon yüklemesi bulur. Hangisini seçeceğini bilemez. Aşağıda bu konuya bir ilişkin bir örnek verilmiştir: * Örnek 1, // Some codes here... void func(long double); void func(char); int main() { float f = 12.4f; func(f); // Bu fonksiyon çağrısı 'ambiguity' tip sentaks hatasına neden olur. // Çünkü 'float' türünden 'long double' için ve 'char' türlerine dönüşüm // 'normal conversion'. DETAYLAR KONUNUN DEVAMINDA. } >>>> 'function overload resolution' üç aşamalı bir süreçtir. >>>>> İlk aşamada derleyici 'candidate functions' ların ismini listeliyor. Bu listelemeyi yaparken de fonksiyonların parametrelerine ve fonksiyon çağrısında kullanılan parametrelere bakmıyor. Sadece yükleme yapılan fonksiyonların isimlerini listeliyor. (NOT: İlgili işbu fonksiyonların isimleri ve bulundukları isim alanları da aynıdır. Aksi halde 'function overloading' söz konusu olmayacaktır.) * Örnek 1, // Some codes here... struct A {}; void func(int); void func(int, int); void func(int* ); void func(double); void func(char); void func(A); int main() { func(12); // Yukarıdaki fonksiyon bildirimlerindeki isimler aynı olduğundan ve aynı isim alanında // bildirildikleri için, bunların hepsi birer 'candidate function'. } >>>>> İkinci aşamada derleyici 'viable functions'ları, 'candidate functions'lara bakarak seçiyor. Seçim yaparken de her bir yüklemeyi tek tek inceliyor. İncelerken de sanki sadece o fonksiyon varmış gibi davranıyor. Eğer fonksiyon çağrısında kullanılan parametre adedi ile 'candidate functions'lardan seçilen tekil fonksiyonun parametre adedi aynı ve her iki fonksiyonun parametre türleri arasında da uyumlu bir dönüşüm var ise, o 'candidate functions' artık bir 'viable functions' haline geliyor. (NOT: Aslında ikinci aşamada şu soruya cevap aranıyor; "Eğer bu fonksiyon tek başına olsaydı, fonksiyon çağrısı legal olur muydu?". Cevap evet ise o fonksiyon 'viable function'.) * Örnek 1, // Some codes here... struct A {}; void func(int); // Sadece bu fonksiyonu ele alalım ve aşağıdaki dört fonksiyon bildirimlerini de yok sayalım. // 'main()' fonksiyon bloğundaki 'func(12)' çağrısı ile bu fonksiyonu biz çağırabiliriz. Dolayısıyla // bu fonksiyon artık bir 'viable function'. void func(int, int); // Sadece bu fonksiyonu ele alalım; 'func(12)' çağrısı ile bu fonksiyonu çağıramayız çünkü parametre // adetleri farklı. Dolayısıyla bu fonksiyon bir 'viable function' DEĞİL. void func(int* ); // Sadece bu fonksiyonu ele alalım; 'func(12)' çağrısı ile bu fonksiyonu da çağıramayız çünkü // parametreler arasında uygun bir 'implicit conversion' yok. Yani 'int' türünden 'int*' türüne // otomatik dönüşüm yok. Dolayısıyla bu fonksiyon bir 'viable function' DEĞİL. void func(double); // Sadece bu fonksiyonu ele alalım; 'func(12)' çağrısı ile bu fonksiyonu ÇAĞIRABİLİRİZ. Çünkü // parametleri arasında uygun bir dönüşüm vardır. Yani 'int' türünden 'double' türüne otomatik // dönüşüm VAR. Dolayısıyla bu fonksiyon bir 'viable function'. BURADA ÖNEMLİ OLAN VERİ KAYBI DEĞİL, // İŞİN LEGALİTESİ. void func(char); // Sadece bu fonksiyonu ele alalım; 'func(12)' çağrısı ile bu fonksiyonu ÇAĞIRABİLİRİZ. Çünkü // parametleri arasında uygun bir dönüşüm vardır. Yani 'int' türünden 'char' türüne otomatik dönüşüm // VAR. Dolayısıyla bu fonksiyon bir 'viable function'. BURADA ÖNEMLİ OLAN VERİ KAYBI DEĞİL, İŞİN // LEGALİTESİ. void func(A); // Sadece bu fonksiyonu ele alalım; 'func(12)' çağrısı ile bu fonksiyonu da çağıramayız çünkü // parametreler arasında uygun bir 'implicit conversion' yok. Yani 'int' türünden 'A' / 'struct A' // türüne otomatik dönüşüm yok. Dolayısıyla bu fonksiyon bir 'viable function' DEĞİL. int main() { func(12); } // ARTIK BU AŞAMADA ELİMİZDE HİÇ 'viable function' YOKSA, 'no-match' TİPİNDE BİR SENTAKS HATASI // ALIRIZ. VELEVKİ SADECE BİR TANE 'viable function' OLSAYDI, O SEÇİLECEKTİ VE // 'function over resolution' TAMAMLANACAKTI. EĞER İKİ YADA DAHA FAZLA 'viable function' VARSA // ELİMİZDE, ÜÇÜNCÜ AŞAMAYA GEÇİYORUZ. >>>>> Üçüncü aşamada 'viable functions' lar arasından bir seçim yapılırsa, o seçim bir 'best-match' olacak ve çözümleme tamamlanacak. Eğer bir seçim yapılamaz ise 'ambiguity' tip SENTAKS HATASI alacağız. Yani bu aşamada fonksiyon çağrısında kullanılan parametrelerin adedi ile fonksiyon imzalarındaki parametrelerin adetleri aynı ve türleri arasında da uyumlu bir dönüşüm söz konusu. Artık derleyici dilin kurallarına dayanarak, parametre türlerini arasındaki dönüşümü öncelik sırasına göre gruplayacak. Bu dönüşümler ise önem sırasına göre şu şekildedir: "variadic conversion < user-defined conversion < standart conversion". Hadi bu grupları da açıklayalım: >>>>>> 'variadic conversion' : Bildirimi yapılan fonksiyonlar 'variadic' parametreye sahip ise, ve daha uygun fonksyion yok ise, fonksiyon çağrısı ile o 'variadic' fonksiyon birbirine bağlanır. * Örnek 1, // Some code here... void func(int, ...); // 'variadic function'. İlk parametreye değer geçmek zorundayız fakat devamında ise bir // tür/adet sınırı yoktur. int main() { func(12); // Yukarıdaki 'variadic function' çağrılır. func(12, 3.4); // Yukarıdaki 'variadic function' çağrılır. func(12, 3.4, 5); // Yukarıdaki 'variadic function' çağrılır. } // BÖYLE BİR DÖNÜŞÜM TÜRÜNÜN ÖNCELİĞİ EN DÜŞÜK OLANDIR. >>>>>> 'user-defined conversion' : Normalde uygun bir 'implicit conversion' yok. Fakat bizim bildireceğimiz bir fonksiyondan dolayı derleyici 'implicit conversion' gerçekleştiriyor. * Örnek 1, struct A{ // (III) : Eğer aşağıdaki gibi bir fonksiyon bildirirsek, derleyici 'implicit conversion' // gerçekleştirecektir. // (IV) : İşte 'implicit conversion' gerçekleşmesini sağlayan fonksiyonun bildirimi. Bu // bildirime dayanarak derleyici otomatik dönüşümü gerçekleştirir. A(int); }; void func(A); int main() { func(12); // (I) : Normal şartlarda bu fonksiyon çağrısı herhangi bir fonksiyona bağlanamaz çünkü // uygun bir 'implicit conversion' yok. // (II) : Yani 'int' türünden 'A' türüne otomatik dönüşüm yapılamıyor. } // BÖYLE BİR DÖNÜŞÜM TÜRÜNÜN ÖNCELİĞİ 'variadic conversion' TÜRÜNDEN DAHA YÜKSEKTİR. >>>>>> 'standart conversion' : Öz ve öz böyle bir dönüşüm vardır. Aklımıza gelen dönüşümler eğer 'variadic conversion' değilse ya da 'user-defined conversion' değilse 'standart conversion' dur. * Örnek 1, // some codes here... "int <==> double" // 'int' türünden 'double' türüne ya da tam tersi. "int* ==> void*" // 'int*' türünden 'void*' türüne. "enum ==> int" // 'enum' türünden 'int' türüne. * Örnek 2, struct A{ A(int); }; void func(A); // (I) void func(double); // (II) int main() { func(12); // Yukarıdaki fonksiyon çağrısı sonrasında, 'function overload resolution' ile (I) ve (II) // numaralı fonksiyonların her ikisi de 'viable functions'. Fakat (II) numaralı fonksiyon // sırasında 'standart conversion', (I) numaralı fonksiyon çağrısında da // 'user-defined conversion' kullanıldığından, derleyici (II) numaralı fonksiyonu // bağlayacaktır. } // BÖYLE BİR DÖNÜŞÜM TÜRÜNÜN ÖNCELİĞİ EN YÜKSEKTİR. EĞER BİRDEN FAZLA 'standart conversion' VARSA, BU DURUMDA DERLEYİCİ BU 'standart conversion' LARI DA KENDİ İÇİNDE GRUPLARA AYIRIR. ÖNEM SIRASINA GÖRE BU TÜRLER; "exact-match > promotion > normal-conversion" şeklindedir. HADİ BU SEFER DE BUNLARI AÇIKLAYALIM: >>>>>>> 'exact-match' : Aşağıdaki hususlar bu kategoriden sayılır: -> Fonksiyon çağrısındaki argümanın türü ile seçilen 'viable function' un parametre türü birbirinin aynısı olma durumu. -> 'array-decay' mekanizması. -> 'const conversion' mekanizması. -> 'function-to-pointer' mekanizması. -> Son olarak 'L-Value to R-Value conversion' mekanizması. Aşağıda bu hususa ilişkin bir örnek verilmiştir: * Örnek 1, // Some code here... void func(int* ); void foo(const int*); int thefunc(int); void thefoo(int(*)(int)); // 'thefoo()' fonksiyonunun parametresi, geri dönüş değeri 'int' türden olan ve aldığı tek // parametrenin de türünün 'int' olduğu bir fonksiyonun adresi. void thefuncTwo(int); int main() { // Array-decay senaryosu: int a[] = {2, 5, 9}; // 'a' dizisinin türü 'int[3]' şeklindedir. func(a); // Dizi ismi bir ifade içinde kullanıldığında, dizinin ilk elemanının adresine dönüşür. // => int[3] => int* // 'func(&a[0]);' şeklindeki çağrıdan bir farkı yoktur. // Parametrenin de türü 'int*' olmuştur. // 'const-conversion' senaryosu: int x = 100; foo(&x); // Burada 'x' nesnesinin türü 'int'. Parantez içerisindeki ifadenin türü de 'int*'. // İlgili 'foo()' fonksiyonunun aldığı argümanın türü ise 'const int*'. // Bu durumda 'int*' türünden 'const int*' türüne dönüşüm sağlanıyor. // 'function-to-pointer' senaryosu: thefunc(); // Normalde bu fonksiyonun türü => "int()(int)" şeklinde. &thefunc(); // 'address-of' operatörünün operandı olduğunda ise türü => "int(*)(int)" şekline geliyor. // Bir fonksiyonun ismi bir ifade içerisinde kullanılırsa eğer, 'function-to-pointer' // mekanizması devreye girer. Yani fonksiyonun ismini, fonksiyonun adresine dönüştürüyor. // Aslında '&func()' şeklinde kullanmışız gibi oluyor. thefoo(thefunc); // Aslında => "thefoo(&thefunc);" // L-Value to R-Value conversion: int y = 100; // 'y' is a L-Value. thefuncTwo(y); // 'y' değişkeninin değerini kullanmak için derleyici, 'L-value' olan değişkeni 'R-value' // haline getiriyor. } >>>>>>> # ÖZETLE AŞAĞIDAKİ DURUMLAR 'exact-match' KABUL EDİLİR # >>>>>>>> Argüman ifadenin türü ile parametrenin türünün birebir aynı olması, >>>>>>>> 'array-decay' dönüşümü, >>>>>>>> 'const-conversion' dönüşümü, >>>>>>>> 'function-to-pointer' dönüşümü, >>>>>>>> 'L-Value to R-Value' dönüşümü. >>>>>>> 'promotion' : İki ayrı kategoride ele alınır. Bir tanesi C dilinden gelen 'integral promotion' ve 'float-to-double promotion'. >>>>>>>> 'integral promotion' : Derecesi(rank) 'int' türünden de aşağıda olan türlerin, işleme sokulmadan evvel 'int' türüne dönüştürülmesi olayıdır. Bu alt türler 'bool', 'signed/unsigned short', 'char', 'signed char', 'unsigned char' türleridir. >>>>>>>> 'float-to-double promotion' : 'float' türünün işleme sokulmadan evvel 'double' türüne dönüştürülmesi işlemidir. >>>>>>> 'normal-conversion' : 'exact-match' ve 'promotion' olmayan bütün dönüşümler 'normal-conversion' şeklindedir. Aşağıda bu konuya ilişkin örnekler verilmiştir: * Örnek 1, // some codes here... // (I) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'promotion', void func(double x); // (II) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion', void func(int x); // (III) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion'. void func(unsigned int x); int main() { func(12.5f); // (I) numaralı fonksiyon çağrılır. } * Örnek 2, // some codes here... // (I) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'promotion', void func(int x); // (II) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion', void func(double x); // (III) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion'. void func(long x); int main() { func('A'); // (I) numaralı fonksiyon çağrılır. } * Örnek 3, // some codes here... // (I) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion', void func(unsigned int x); // (II) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion', void func(long double x); int main() { // Her ikisi de 'normal-conversion' olduğundan 'ambiguity' oluşur ve SENTAKS hatasıdır. func(12); } * Örnek 4, // Some codes here... // (I) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion', void func(long double); // (II) : 'func()' çağrısına bu fonksiyonun bağlanma durumuna 'normal-conversion', void func(char); int main() { float f = 12.4f; // Her ikisi de 'normal-conversion' olduğundan 'ambiguity' oluşur ve SENTAKS hatasıdır. func(f); } >> 'FO' var mı yok mu sorusuna cevap aranır. Bu durumda FONKSİYON İSİMLERİ VE BİLDİRİLDİKLERİ İSİM ALANLARI AYNI OLACAK FAKAT PARAMETRİK YAPISI FARKLI OLACAK. Bu üç şartı da sağlıyor ise 'FO' VARDIR. Aksi halde 'FO' yoktur. >>> Eğer 'FO' var ise fonksiyon çağrısının hangi fonksiyon yüklemesine bağlanacağı aşağıdaki kural setleri ile belirlenir. >>>> İlk önce 'FO'ya dahil edilen fonksiyon isimleri, parametrik yapılarına ve geri dönüş değerlerine bakılmaksızın, alt alta listelenir. >>>> Daha sonra bu fonksiyon çağrısında kullanılan parametrelerin türleri ve adetleri, listelenmiş olan fonksiyonlar ile tek tek karşılaştırılır. Bir diğer değişle "Eğer bu fonksiyon tek başına olsaydı, fonksiyon çağrısı legal olur muydu?" sorusuna cevap aranır. Eğer cevap EVET ise bir sonraki aşamaya geçilir. >>>>> Artık bu aşamada fonksiyon çağrısında kullanılan parametreler ile fonksiyon imzasında kullanılan argümanlar arasındaki 'uyumlu dönüşüm' irdelenecektir. Bu aşama, kendi arasıda ÜÇ FARKLI kural seti tanımlar; artık derleyici dilin kurallarına dayanarak, parametre türlerini arasındaki dönüşümü öncelik sırasına göre gruplayacak. Bu dönüşümler ise önem sırasına göre şu şekildedir: "variadic conversion < user-defined conversion < standart conversion". Şimdi bunları açıklayalım. >>>>>> 'variadic conversion' : Fonksiyon imzasında 'variadic parametre' olmasından dolayı, parametreler ile argümanlar arasındaki ilişkiye hitap eder. Eğer birden fazla bu dönüşüm var ise 'ambiguity' sentaks hatası alırız. >>>>>> 'user-defined conversion' : Normalde dilin kuralları gereği bahsi geçen bir dönüşüm sentaks hatası. Fakat bizim yazacağımız üçüncü bir fonksiyondan dolayı, kod legal hale geliyor. Eğer birden fazla bu dönüşüm var ise 'ambiguity' sentaks hatası alırız. >>>>>> 'standart conversion' : Yukarıdaki iki dönüşüm haricindeki diğer dönüşümler ise bu dönüşüm kategorisindedir. Eğer birden fazla bu dönüşüm var ise aşağıdaki kurallara göre bir seçim yapılır: "exact-match > promotion > normal-conversion". Şimdi bunları açıklayalım. >>>>>>> 'exact-match' : Aşağıdaki şartlar bu dönüşümü ifade eder: -> Argüman ifadenin türü ile parametrenin türünün birebir aynı olması, -> 'array-decay' dönüşümü, -> 'const-conversion' dönüşümü, -> 'function-to-pointer' dönüşümü, -> 'L-Value to R-Value' dönüşümü. >>>>>>> 'promotion' : Kendi içinde iki farklı 'promotion' barındırır: >>>>>>>> 'integral promotion' : Derecesi(rank) 'int' türünden de aşağıda olan türlerin, işleme sokulmadan evvel 'int' türüne dönüştürülmesi olayıdır. Bu alt türler 'bool', 'signed/unsigned short', 'char', 'signed char', 'unsigned char', türleridir. >>>>>>>> 'float-to-double promotion' : 'float' türünün işleme sokulmadan evvel 'double' türüne dönüştürülmesi işlemidir. >>>>>>> 'normal-conversion' : 'exact-match' ve 'promotion' olmayan bütün dönüşümler 'normal-conversion' şeklindedir. >> 'Default argument' kullanılması her ne kadar 'FO' yu tetiklese de zaman zaman 'ambiguity' tip SENTAKS hatasına neden olabilir. * Örnek 1, // some codes here... void func(int x, int y = 100); // I void func(int x); // II int main() { func(12); // Sentaks hatası. } >> Parametrelerin 'call-by-value' veya 'call-by-reference' olması birbirine üstünlük taslamamaktadır. * Örnek 1, // some codes here... void func(int& x); // I void func(int x); // II void foo(const int& x); // III void foo(int x); // IV int main() { int a = 10; func(a); // 'ambiguity' tip sentaks hatası. func(25); // II numaralı fonksiyon çağrılır. Çünkü I numaralı fonksiyon 'viable' DEĞİL. foo(50); // 'ambiguity' tip sentaks hatası. } >> Parametrelerin 'L-Value Referance' veya 'R-Value Referance' olmaları durumunda çağrım: * Örnek 1, // some codes here... void func(const int& x); // I void func(int&& x); // II int main() { int a = 10; func(a); // I numaralı çağrılacak. (Copy-Semantics) func(50); // II numaralı çağrılacak. (Move-Semantics) C++ 11 ile dile geldi. } * Örnek 2, // some codes here... void func(int& x); // I void func(const int& x); // II void func(int&& x); // III int main() { int x = 10; const int y = 20; func(x); // I func(y); // II numaralı çağrılacaktır. I numaralı fonksiyon 'viable' DEĞİL. func(50); // III } Buradan hareketle diyebiliriz ki 'const' nesneler için 'const-overload' versiyonlar çağrılırken, 'non-const' olanlar için de uygun olanlar. >> Parametrelerin 'void*' ve 'bool' olma durumu: * Örnek 1, // some codes here... void func(void* x); // I void func(bool x); // II int main() { int* ptr = nullptr; func(ptr); // Yukarıdaki her iki fonksiyon da ayrı ayrı olsaydı sentaks hatası olmayacaktı. Çünkü C++ dilinde 'int*' // türünden 'void*' türüne ve 'int*' türünden 'bool' türüne otomatik tür dönüşümü vardır. // 'nullptr' değerindeki göstericiler, 'bool' türüne dönüşürken 'false' değerini alıyor. Diğer durumlarda // 'true' değerini alıyor. // FAKAT DİLİN KURALLARI GEREĞİ, 'void*' TÜRÜNE DÖNÜŞÜMÜN SEÇİLEBİLİRLİĞİ DAHA YÜKSEK OLDUĞUNDAN I NUMARALI // FONKSİYON ÇAĞRILIR. } >> Birden çok parametresi olan fonksiyonların yüklemelerinin seçilmesinde aşağıdaki kural seti aranır: >>> Bir parametre diğerlerine üstünlük sağlayacak; diğer parametreler ise dönüşüm üstünlüğü bakımından, diğerlerinden kötü olmayacak. * Örnek 1, // some codes here... void func(int, int, double); // I void func(long, float, int); // II void func(double, double, double); // III int main() { func(12, 56L, 3.4); // i. '12' değerinin türü 'int'. I numaralı bildirim ile 'exact-match'. Dolayısıyla fonksiyon // çağrısındaki diğer parametreler, yukarıdaki bildirimlerde kullanılan parametrelerden daha kötü // olmayacak. // ii. '56L' değerinin türü 'long'. Her üç fonksiyonda da 'long' türünden sırasıyla 'int', 'float' ve // 'double' türlerine dönüşüm 'normal-conversion'. Dolayısıyla bu parametre, diğerlerinden kötü değil. // iii. '3.4' değerinin türü 'double'. I ve III 'exact-match' fakat I numaralı fonksiyonun parametreleri // daha uygun olduğundan, // GÜNÜN SONUNDA I numaralı seçilir. } * Örnek 2, // some codes here... void func(int, int, float); // I void func(long, float, int); // II void func(double, double, double); // III int main() { func(12, 56L, 3.4); // i. '12' değerinin türü 'int'. I numaralı bildirim ile 'exact-match'. Dolayısıyla fonksiyon // çağrısındaki diğer parametreler, yukarıdaki bildirimlerde kullanılan parametrelerden daha kötü // olmayacak. // ii. '56L' değerinin türü 'long'. Her üç fonksiyonda da 'long' türünden sırasıyla 'int', 'float' ve // 'double' türlerine dönüşüm 'normal-conversion'. Dolayısıyla bu parametre, diğerlerinden kötü değil. // iii. '3.4' değerinin türü 'double'. I numaralı 'normal-conversion' fakat III numaralı 'exact-match'. // Dolayısıyla üçüncü parametre için III numaralı daha iyi. // GÜNÜN SONUNDA 'ambiguity' tip SENTAKS hatası alırız. }