top of page

C++ Dilinde Function Overloading

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Oct 14, 2023
  • 5 min read

C++ dilinde overloading kurallarını operatör ve fonksiyon çağrı operatöründe inceledik. Bu yazımızda fonksiyonların overload edilmesine değineceğiz. Yine bazı temel kurallar çerçevesinde bazı istisnai durumlarıyla beraber function overloading konusu C++ dilinde oldukça önemli bir konudur. Tasarım yaparken kullanışlı ve bazı noktalarda maliyet açısından faydaları da olacaktır.

Öncelikle temel kurallarını yazarak örneklerle pekiştirelim...


  1. İlk kuralımız olarak overload edilen fonksiyonlar aynı scope içerisinde olmalıdır. Aksi takdirde overload olarak değerlendirilmeyecek ve kuralları geçerli olmayacaktır.

  2. Bu fonksiyonların(overload edilen) imzalarının farklı olması şarttır. Parametre sayısı ve/veya türü farklı olmak zorundadır.

  3. Herhangi bir run-time maliyeti yoktur. C++ derleyici doğru fonksiyonun(viable) çağrılması kararını derleme aşamasında çözecektir. Yani early binding söz konusudur.

  4. Redecleration durumuna dikkat edilmelidir.


Redefinition ya da redeclaration ...
double redeclare_func(std::string);
double redeclare_func(std::string);
*****************************
AÇIKLAMA: burada fonksiyonları aynı scope içerisinde düşünürsek, imzaları aynı olduğu için overloading değil redecleration durumu oluşacaktır. Kullanım durumuna göre hata ya da tanımsız davranış oluşacaktır.
*****************************
CEVAP: redefinition
*****************************

#include <iostream>

using namespace std;

typedef int Type;
using SmartCode = int;

void redeclare_func(int x) 
{
    cout << "redeclare_func(int) called. val: " << x << endl;
}

void redeclare_func(Type x) 
{
    cout << "redeclare_func(Type) called. val: " << x << endl;
}

void redeclare_func(SmartCode x) 
{
    cout << "redeclare_func(SmartCode) called. val: " << x << endl;
}

int main() 
{
    int a{1};
    Type b{3};
    SmartCode c{5};

    redeclare_func(a);  // redeclare_func(int) calls.
    redeclare_func(b);  // redeclare_func(Type) calls.
    redeclare_func(c);  // redeclare_func(SmartCode) calls.

    return 0;
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: redefinition
*****************************

#include <iostream>

using namespace std;

void redeclare_func(const int idx) 
{
   std::cout << "First " << idx << endl;
}

void redeclare_func(int idx)       
{
   std::cout << "Second " << idx << endl;
}

int main() 
{
   int idx{5};
   redeclare_func(idx);      
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: redefinition
*****************************

#include <iostream>

using namespace std;

void redeclare_func(int* const ptr) 
{
   std::cout << "First " << *ptr << endl;
}

void redeclare_func(int* ptr)       
{
   std::cout << "Second " << *ptr << endl;
}

int main()
{
   int idx{5};
   redeclare_func(&idx);           
}
*****************************
AÇIKLAMA: top level const
*****************************
CEVAP: redefinition
*****************************

#include <iostream>

void redeclare_func(int val) 
{
    std::cout << "redeclare_func(int) called val = " << val << std::endl;
}

void redeclare_func(int val = 5) 
{
    cout << "redeclare_func(int) def param called val = " << val << endl;
}

int main() 
{
    redeclare_func(3);   
    
    return 0;
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: redefinition
*****************************

Syntax hatası olan durum ...

int err_func(int);
double err_func(int);
*****************************
AÇIKLAMA: bu durum function overloading değil redeclaration değil doğrudan sentaks hatasıdır.
*****************************
CEVAP: syntax error
error: conflicting declaration 'double err_func(int)'
*****************************


Function overloading senaryoları ...

int overload_func(int);
int overload_func(int, int);
*****************************
AÇIKLAMA: aynı isimli aynı scope içerisinde ve imzaları farklı overload fonksiyonlardır.
*****************************
CEVAP:  legal
*****************************

#include <iostream>

using namespace std;

void overload_func(const int* ptr) 
{
   std::cout << "First " << *ptr << endl;
}

void overload_func(int* ptr)       
{
   std::cout << "Second " << *ptr << endl;
}

int main()
{
   int idx{5};
   overload_func(&idx);           
}
*****************************
AÇIKLAMA: low level const
*****************************
CEVAP: legal - const overloading
*****************************

#include <cstdint>

void compiler_func(int val);
void compiler_func(int32_t val);
*****************************
AÇIKLAMA: int32_t typedef bildirimi olduğu için derleyiciye bağlı değişkenlik gösterecektir.
*****************************
CEVAP:  ?
*****************************

void overload_func(int val);
void overload_func(bool val);
*****************************
AÇIKLAMA: 
*****************************
CEVAP:  overload
*****************************

void overload_func(int val);
void overload_func(int& val);
*****************************
AÇIKLAMA: 
*****************************
CEVAP:  overload
*****************************

void overload_func(int& val);
void overload_func(int&& val);
*****************************
AÇIKLAMA: 
*****************************
CEVAP:  overload
*****************************

void overload_func(char);
void overload_func(signed char);
void overload_func(unsigned char);
*****************************
AÇIKLAMA: her biri distinct türler olduğu için 3 farklı overload vardır diyebiliriz.
*****************************
CEVAP:  overload
*****************************

  • FUNCTION OVERLOAD RESOLUTION

Yukarıdaki örneklerde verildiği gibi aynı isimli farklı imzaları olan fonksiyonlar tanımlayarak çağrı yapabiliyoruz. Function overload resolution terimi de hangi fonksiyona çağrı yapıldığının sürecidir ve derleme zamanında gerçekleştirilir. Çağrı yapılan birden fazla fonksiyon arasından derleyicinin seçim yapması ambiguity olarak ifade edilmektedir. Aksi durumda da hiçbir çağrının ilgili fonksiyonlara uygun olmaması durumu gerçekleşir.

Bu fonksiyonlar değerlendirilirken tek olduğu varsayıldığında çağrılabilir olan overloadlar viable olarak değerlendirilir.

Çağrı yapılan bu fonksiyonlar değerlendirilirken :
  1. Yapılan çağrı ilgili fonksiyonunun parametre sayısı ile aynı olmalıdır. (default argument ve variadic hariç)

  2. Yapılan çağrı ile otomatik tür dönüştürme uygun olmalıdır.


void overload_func(int*);

int main ()
{
    void* vptr = nullptr;

    overload_func(vptr);
}
*****************************
AÇIKLAMA: çağrılabilir değil çünkü void* türünden int* gibi primitive türlere dönüşüm sentaks hatasıdır.
*****************************
CEVAP:  error: invalid conversion from ‘void*’ to ‘int*’
*****************************


void overload_func(int);
void overload_func(double);

int main ()
{    
    func(15L);
}
*****************************
AÇIKLAMA: her iki overload geçerli olduğu için ambiguity.
*****************************
CEVAP:  call of overloaded ‘overload_func(long int)’ is ambiguous
*****************************

void overload_func(long double);
void overload_func(char);

int main ()
{    
    func(15L);
}
*****************************
AÇIKLAMA: her iki overload geçerli olduğu için ambiguity.
*****************************
CEVAP:  call of overloaded ‘overload_func(double)’ is ambiguous
*****************************


  • ARGÜMANDAN ÇAĞRIYA YAPILAN DÖNÜŞÜMLER

  1. exact match

  2. promotion

  3. conversion

Burda bahsedilen dönüşümler overloading sürecinde derleyici tarafından değerlendirilir. En az tercih edilen ve önerilmeyen dönüşüm variadic dönüşümdür. Derleyici seçim yapması durumunda variadic olmayan overload fonksiyonu seçer. Daha sonra en az tercih edilen user defined olacaktır.


SFINAE ilkesi ile değerlendirilen ve hataların görmezden gelindiği bir kod örneği :
#include <iostream>

void sfinae_func(int val)
{
    std::cout << "non_temp";
}

template<typename... T> 
void sfinae_func(int val, T...)
{
    std::cout << "temp";
}

int main()
{
    sfinae_func(12);
  
    return 0;
}
*****************************
AÇIKLAMA: Bu örnekte T... türü boş olduğunda işlev uyumsuz hale gelir ancak SFINAE ilkesi gereği derleyici tarafında görmezden gelinir ve hatasız şekilde çalışmaya devam eder.
*****************************
CEVAP:  non_temp
*****************************

user defined conversion örneği ...
struct SmartCode {
    int val;
};

void func(SmartCode);

int main ()
{
    func(5);
}
*****************************
AÇIKLAMA: int tür sınıf türüne dönüşüm yapılmaya çalışılmış ve hata durumu ortaya çıkmıştır. Böyle bir otomatik tür dönüşümü geçerli değildir.
*****************************
CEVAP:  could not convert ‘5’ from ‘int’ to ‘SmartCode’
*****************************

struct SmartCode {
    SmartCode(int val) : m_val{val} {}

    int m_val;
};

ostream& operator<<(ostream& os, const SmartCode& oth)
{
    return os << oth.m_val;
}

void func(SmartCode sc)
{
    std::cout << "sc : " << sc << std::endl;
}

int main ()
{
    func(5);
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: sc : 5
*****************************

Standart Conversion : 1. double to int 2. long to float 3. bool to int

#include <iostream>

using namespace std;

void func(int* ptr)
{
    cout << *ptr << endl;
}

int main()
{
    int arr[4]{4};
    func(arr);
}
*****************************
AÇIKLAMA: burada exact match durumu vardır. Yani çağrı ile fonksiyon tümüyle aynıdır. 
1. int to int 
2. T* to const T* 
3. array to pointer conversion
*****************************
CEVAP: 4
*****************************
function to pointer conversion
#include <iostream>

using namespace std;

void func(int(*)(int))
{
    cout << "func pointer" << endl;
}

int foo(int val)
{
    return val;
}

int main ()
{
    func(foo);
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 
*****************************

L value to R value conversion
#include <iostream>

using namespace std;

void func(int val)
{
    cout << "val :" << val << endl;
}

int main ()
{
    int a = 5;

    func(a);      // a L value 
    func(10);
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 
*****************************

promotion : int altı türlerin int türüne yükseltilmesidir.(integral promotion) - signed short - unsigned short - bool - char - signed char - unsigned char promotion : float to double

#include <iostream>

using namespace std;

void func(double)    
{
    cout << "promotion" << endl;
}

void func(int)    
{
    cout << "conversion" << endl;
}

int main()
{
    func (12.f);
}
*****************************
AÇIKLAMA: float veri türü double türüne yükseltilmiştir.
*****************************
CEVAP: promotion
*****************************

void func(unsigned int);
void func(long double);

int main()
{
    func(5);
}
*****************************
AÇIKLAMA: her ikisi de conversion ve ambiguity olur.
*****************************
CEVAP: 
*****************************

Birden fazla parametreli overload fonksiyonlar : -> Standartlarda overload fonksiyonlara çağrı yapıldığında bir fonksiyon ambiguity olmadan seçilebilmesi için en az bir parametrede diğerlerine göre avantajlı olması gerekmektedir. -> Diğer parametrelerde de daha az avantajlı olmayacak ve eşit olabilecektir.

#include <iostream>

using namespace std;

void func(int, double, long)
{
    cout << "(1)int, long, double" << endl;
}

void func(double, int, int)    
{
    cout << "(2)double, int, int" << endl;
}

void func(float, float, float)
{
    cout << "(3)float, float, float" << endl;
}

int main()
{
    func(12, );      // 1 numaralı daha iyi durumda
    
    // ambiguity oldu. çünkü 12 1 numara için, 13 2 numara için en uygun.
    func(12, 13, );  
    
    // uint->double:conversion, uint->int : conversion, uint->float:conversion
    // yani 2.argümana göre de 1numaralı fonk. kötü durumda değil.
    func(12, 6u, );  
           
    func(12, 6u, 3.4);   // 1.fonk. çağırılır.
    func(3.4, 4u, 5.4);  // 2.fonk. çağırılır.
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 
*****************************

ree


 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comentários


bottom of page