C++ Function Overloading & Function Overloading Resolution

16 minute read
  • Konu içerisinde function overloading ve resolution mekanizmalarına değineceğiz. Derleyicinin arka planda bunu nasıl yaptığını, hangi aşamalardan geçtiğini anlatmaya çalışacağım.

  • Fonksiyonların aşırı yüklenmesi çevirisi yanlış bir anlama çıkabiliyor. Aşırı olan birşey iyi değildir. Burada fonksiyonların yüklenmesi diyelim.

  • C++ dilinde bu mekanizma var, C dilinde yok.

  • Function overloading basit olarak aynı isimli fonksiyonların bir arada bulunmasıdır.

  • Amaç: Client’ın işini kolaylaştırmak.


C dilinde bu mekanizmanın olmaması ne gibi sıkıntılar çıkarıyor ve neden yok?

  • C dilinde böyle bir yapının olmamasından dolayı şöyle sıkıntılar çıkmıştır. Her fonksiyon için ayrı bir isim oluyor. Örneğin mutlak değer fonksiyonu abs(), floating-point number için fabs(), long int için labs().. işte böyle bir sıkıntı söz konusu.

  • C dilinde olmamasının sebebi derleyicinin kodunu küçük tutmak. Çünkü böyle bir yapıda derleyecinin kodu çok daha fazla büyüyecek. Bu karmaşık derleyici yoran bir mekanizma. Derleyicisi en az %30 kadar büyütürdü.

  • Runtime maliyeti yok, çünkü ne kadar aynı isimli fonksiyon olsa da hangi fonksiyonun çağırıldığı, derleyici compile time da anlıyor. Yani linker a dışsal referans olarak bunu anladıktan sonra yazıyor.

  • Hangi fonksiyon çağrıldığı anlama, compile time da static binding yada early binding denir. Bind (ilişkilendirme, bağlama)


Dynamic binding | C++ OOP en önemli araçları

  • Late binding | Çalışma zamanı çokbiçimliliği (Runtime Polymorphism)

Derleyicinin hangi fonksiyonu çağırdığını nasıl anlar?

  • (Function Overload Resolution) derleyicinin dilin kurallarına dayanarak hangi fonksiyonu çağrıldığını anlama işine denir

  • (Fonksiyon imzası) bildirimden tür çıkartım, geriye kaç parametre, kaç değişken var ve türleri neler gibi.

  • İmza terimini duyduğunuzda, fonksiyon geri dönüş değerinin türü dahil değil.

  • Bildirim dediğimizde fonksiyon geri dönüş değeri türü dahil.


Fonksiyon Overloading dendiğinde 3 koşulun olması gerekiyor.

  1. Fonksiyon isimleri aynı olacak.
  2. Scopeları aynı olucak (aynı scope larda fonksiyonlar birbirini overload eder, farklı scopelardaki fonksiyonlar birbirini maskeler)
  3. İmzaları farklı olucak (parametre sayısı veya parametre turu)
  • Aynı scope ta aynı isimli 2 fonksiyon varsa ve function overloading yoksa 2 şey olabilir, ya error yada redeclaration(yeniden bildirim)

  • Function Overloading için distinct type olmalıdır.

  • C++11 de typedef bildirimlerinin yerine uydurulan ayrı bir araç var.

  • typedef isimleri distinct tür değildir, bu yüzden typedef isimleriyle function overload olmaz. C++11 de artık typedef bildirimlerinin yerine uydurulan ayrı bir araç var. “using Kerem int” gibi.

  • using bildirimi, typedef te olmayan ilave anlamlarda var.

 1typedef int Word;
 2
 3int f1(int);
 4int f1(Word); // Yeniden Bildirim (Redeclaration)
 5
 6// IMZALAR ayni, GERI DONUS DEGERLERIDE ayni
 7typedef int Word;
 8
 9int f1(int);
10
11double f1(Word); // GECERSIZ -> (Type Mismatch in Redeclaration)
12
13void func(const int x);
14void func(int x); // Yeniden Bildirim (Redeclaration)

Asla pointer olmayan bir parametreyi const yapmayın. Yaparsak ne olur?

  • Fonksiyon bildirimi var yada yok. Derleyici burada ki const anahtar sözcüğünü ciddiye almaz.

  • Neden ciddiye almaz? Çünkü bu call by value bir anlam yüklersek, x değerini değiştiremem der ve sen onu değiştirsende değiştirmesende benim değişkenime olan birşey yok. O yüzden beni ilgilendirmiyor der.

    1. pointer olmayan parametreleri bildirimde veya tanımda const yapmayın onun bir anlamı yok.
    2. derleyici bunu görmez
  • Eğer 2 parametre türleri aynı ise pointer değilse ve biri T türünden ise öteki const T türündense function overloading değil, errorde değil, redeclaration’dır. Bu cümle biraz karışık gelmiş olabilir. Örnek vererek acıklayalım.

 1void func(const int x);
 2void func(int x); // Yeniden Bildirim (Redeclaration)
 3
 4
 5// Geçerli, Const Overloading (çok kullanılan bir mekanizma)
 6void func(int &);
 7void func(const int &);
 8
 9
10// Geçerli, Pointer Overloading
11void func(int *);
12void func(const int *);
13
14
15// Geçerli, Biri CALL BY VALUE digeri VALL BY REFERENCE
16void func(int);
17void func(int &);
18
19
20// Geçerli, Funtion Overloading (Default Parameter)
21void func(int);
22void func(int, int = 10);

Ufak tekrar (function overloading için olması gereken):

  1. Scope aynı olacak.
  2. İsimler aynı olacak.
  3. İmzalar farklı olacak.
  • enum -> int türüne dönüşüm var.

  • doğal türlerden -> enum türüne dönüşüm yok.

  • Farklı türden pointerlar arası dönüşüm yok

1T *        ->     void * a dönüşür
2void *     ->     T * a dönüşmez
  • Bir pointera tam sayı olarak 0 atarsanız tam sayı olan 0 null adresine dönüşür.

FUNCTION OVERLOAD RESOLUTION

  • Derleyicinin hangi fonksiyonu çağrıldığını anlama mekanizmasıdır.

  • İki şekilde sonuçlabilir. (ERROR yada LEGAL)

  • Yani ortada bir function overloading var diye yapılan her çağrı doğru olmayabilir.

  • Error ise 2 seçenek var,

  1. Yanlış function çağrılması(no match),
  2. Ambiguity (Çift anlamlılık hatası)
  • Aday fonksiyonlar,
  1. İlk aşamada sadece aynı isimde olmaları yeterli. Aynı scope içinde olucak. Farklı scope içinde ise maskeleme olur.
  2. İkinci aşamada derleyici uygun fonksiyonları bulmak ister. Bu aşamada hiç uygun fonksiyon bulamassa ERROR olur. Burada ki aşama uygun fonksiyonları bulup eleme yapma.
  3. Birden fazla uygun fonksiyon varsa, derleyici dilin kurallarına göre argümanlardan parametre değişkenlerine yapılan dönüşümü belirli kalite grubuna ayıracak. Bunlara bir renk vericek gibi düşünebiliriz. Yani her argümandan her parametre değişkenine dönüşümün kalitesi aynı değil. Eğer fonksiyon çağrısındaki argümanın diyelim ki birinci fonksiyona dönüşümü A kalitesinde ise ikinci fonksiyonun ilgili parametresine dönüşümü ondan daha düşük B kalitesinde ise A seçilecektir.

Yani bu aşama da derleyici fonksiyonları derecelendirecek. Bu derecelendirmeyi neye göre yapıyor?

  • Bir argümandan bir parametre değişkenine legal olarak atama yapılabiliyorsa dönüşümün kalitesi 4 kalite gurubundan biri olacaktır. (Yüksek kaliteden -> Düşük kaliteye)
11. EXACT MATCH (Tam Uyum)
22. PROMOTION (Terfi — Yükseltme)
33. STANDARD CONVERSION (Standard Dönüşüm)
44. USER DEFINED CONVERSION
  • Bir argüman bir parametre değişkenine değer atanırken, derleyici function overloading seçimi yaparken rank vericek, derecelendirecek
  • İkiside aynı ranka sahip ise bu ambiguity(en çok 3. ve 4. case için ambiguity oluyor)

EXACT MATCH

  1. Exact match tam uyumluluk olarak çevirebiliriz. Örneğin int argümanı int parametreye göndermek, float’u float’a göndermek exact matchtir.
  • Bazı özel durumlarda exact match kabul edilir,
  1. Eğer fonksiyon parametresi const T ise bu fonksiyonu T ile çağırırsak T dan const a dönüşüm exact match kabul edilir.
  2. func(int x); int y = 10; func(y); // sol taraf değerinden sağ taraf dönüşüm exact math l value ~> r value translation.
  3. dizi isimlerini sizeof() operatörü ptr gönderilmesi sonucunda dizinin ilk elemanı adrese dönüştürülüyor.
 1void func(int *ptr);
 2
 3int a[100];
 4func(a);
 5func(&a[0]); // Array to Pointer Conversion
 6int func(int);  // &func -> fonksiyon adresi
 7int(*)(int);    // Adres func -> &func turu aslinda bu
 8
 9// C dilinde bir hatirlatma
10#include <iostream>
11
12int func(int, int);
13
14int main()
15{
16    int a[100];
17
18    int *ptr = a; // ARRAY TO POINTER CONVERSION, 0 indisli elemanin adresine donusturuluyor
19
20    int (*fp)(int, int) = func; // FUNCTION TO POINTER CONVERSION
21            // &func demek yerine -> func demek yeterli
22}

PROMOTION

  • int altı türlerin, int’e yükseltilmesi(c dilinde var)(integral promotion)

  • int altı türler(short, char, bool) int’e yükseltilmesi (Yeni gelen char türleri hariç.)

  • float türünden double dönüşümde promotion

  • Dikkat double türünden long double’a vs dönüşümleri promotion değil

  • int türünden unsigned int ve int türünden double dönüşümleri promotion değil


STANDARD CONVERSION

  • Argümandan parametre dönüşümü legal ama exact match ve promotion değil ise o zaman standard conversion’dır.

  • Örnek int türünden double atama legal

  • unsigned int türünden int türüne standard conversion

  • 0’dan pointer’a standard conversion

1void func(double);
2
3func(12);   // STANDARD CONVERSION
4
5void func(unsigned int);
6// int -> double       STANDARD CONVERSION
7// int -> unsigned     STANDARD CONVERSION
8
9//  İkiside Standard Conversion o zaman ambiguity

USER DEFINED CONVERSION

  • C dilinde olan birşey değil ama C++ dilinde var.

  • User define conversion, programcı tarafından oluşturulan tanımlanan dönüşümlerdir.

  • Bir dönüşüm, biz fonksiyon yazdık diye yapılıyorsa buna user defined conversion denir. O fonksiyon yazılmasaydı legal olmayacaktı.

 1struct Data{};
 2void func(struct Data);
 3func(10); // LEGAL DEGIL ERROR
 4// error: no viable conversion from 'int' to 'struct Data'
 5
 6// Conversion Constructor
 7Data(int); // Bu fonksiyonun olmasi donusumu LEGAL KILIYOR
 8func(10); // LEGAL
 9#include <iostream>
10
11void func(int *ptr)
12{
13    std::cout << "func(int *)" << std::endl;
14}
15
16void func(const int *ptr)
17{
18    std::cout << "func(const int *)" << std::endl;
19}
20
21int main() {
22
23    int x = 10;
24    const int y = 20;
25
26    func(&x); // usteki void -> func(int *ptr)
27    func(&y); // alttaki func cagirilacak void -> func(const int *ptr)
28
29    return 0;
30}
31#include <iostream>
32
33void func(int &r)
34{
35    std::cout << "func(int &)" << std::endl;
36}
37
38void func(const int &r)
39{
40    std::cout << "func(const int &)" << std::endl;
41}
42
43int main() {
44
45    int x = 10;
46    const int y = 20;
47
48    // nesnelerin kendileri ile cagiracagiz
49    func(x); // int & olan
50    func(y); // cinst int & olan
51
52    return 0;
53
54}
55#include <iostream>
56
57/*
58 * call by ref ve value'da FUNC OVERLOAD OLUSTURUYOR
59 * call by val'nun vall by ref'a USTUNLUGU YOK
60 */
61
62void func(int &r)
63{
64    std::cout << "func(int &)" << std::endl;
65}
66
67void func(int)
68{
69    std::cout << "func(int )" << std::endl;
70}
71
72int main() {
73
74    int x = 10;
75    const int y = 20;
76
77    func(x);  // CIFT ANLAMLILIK HATASI
78    // error: call to 'func' is ambiguous
79
80    return 0;
81
82}