top of page

C++ Dilinde const ve constexpr Keywords

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • May 18, 2023
  • 9 min read

Updated: May 22, 2023

Bu yazıda temelde sabit ifadeler değişmez ya da değiştirilmesi istenmeyen durumlarda(değişken, fonksiyon...) kullanılan anahtar kelimelere değinilmektedir. Önceden de ifade ettiğimiz üzere C programlama dili tüm eklentileri birebir olmasa da C++ programlama dili içerisinde yer almaktadır. const anahtar sözcüğü de bunlardan biridir. ilerleyiş olarak önce const keyword nedir, nerelerde ve nasıl kullanılır sorularına cevap vermeye çalışacağız. Sonrasında modern C++(11) ile dile eklenen constexpr anahtar sözcüğü nedir nasıl kullanılır ve const keyword ile farkları nedir sorularına cevap vereceğiz.


İlk olarak const anahtar sözcüğünü literatür olarak ele alırsak; bir değişkenin değerinin sabit olduğunu belirtmek için kullanılır. Bir değişken const olarak tanımlandığı zaman değeri bir kez atandıktan sonra değiştirilemez. const değişkenlere derleme zamanında değer ataması yapılır.

Önceki yazılardan da bildiğimiz üzere C++ dilinde const nesneye ilk değer vermek zorunludur. Aksi durum syntax errror olarak bize dönecektir.

int main()
{
    const int val;
}
*****************************   
CEVAP :  syntax errror
*****************************

  • low level const ile high level const farkı da bu konuyu anlamakta önem arz etmektedir. const(sabit) değiştirelemez veriyi gösterir demiştik. İfadeyi yıldız (asterix) işaretinden ikiye ayırınca; const eğer sol tarafta ise işaretlenen veri için, sağ tarafta ise işaretçinin (pointer) kendisi içindir:

const int* low;
int *const high;
  • Bu örnekte "low" sabit bir integer gösteren işaretçidir, neyi işaret edilen sayı değiştirebilir ancak sayının kendisi değiştirilemez. "high" ise sabit bir işaretçidir, kendisi değişemez ama sayı değişebilir. İlk const verinin istediğiniz tarafında olabilir; yani "const int*" ile "int const*" aynı anlama gelir.


C dilinde constant expression kullanımının zorunlu olduğu yerlere değindikten sonra örneklerle pekiştirmeye çalışalım.

  1. Static ömürlü değişkenlerin initializer'ı olan ifadeler.

  2. Dizi boyutu belirtilen ifadeler(VLA(varible length array) söz konusu değil ise)

  3. Switch deyiminin case ifadeleri

  4. Dizilerin initializer ifadeleri

  5. Yapıların (struct) bit-field ifadeler için ne kadar yer kaplayacağını gösteren yerler.

  6. Global const nesneler external linkage durumuna aittir. Diğer modüller tarafında isimleriyle kullanılabilirler. C++ dilinde iç bağlantıya aittirler(internal linkage).

  • Aşağıdaki örnekler sırayla yukarıdaki maddeler için verilen örneklerdir :

/* 1 */
 
int val = 5;
int m_val = val;       

int main()
{
    return 0;
}
*****************************   
AÇIKLAMA : C dilinde geçerli değildir. static ömürlü x değişkenine ancak constant expresion ile ilk değer verilir.
*****************************
/* 2 */

int main()
{
    const int val = 5;

    int arr[val] = { 0 };
}
*****************************   
AÇIKLAMA : C++ dilinde geçerli, C dilinde syntax error.
*****************************
/* 3 */

switch(state) 
{
    case state_one: 
    break;   
}
*****************************   
AÇIKLAMA : state_one constant expression değil ise C ve C++ dillerinde syntax error.
*****************************
/* 4 */

int main()
{
    int arr[2] = { val, val1 }; 
}
*****************************   
AÇIKLAMA : val ve val1 constant expression olmalıdır.
*****************************
/* 5 */

struct Smart_Code {
    int val : bt;        
}; 
*****************************   
AÇIKLAMA : bt constant expression olmalıdır.
*****************************
/* 6 */

const int val = 5;

int main()
{
    return 0;
}
*****************************   
AÇIKLAMA : C dilinde yazılan üstteki const int val = 5; ifadesi C++ dilinde static int val = 5; gibi algılanır.
*****************************
  • C++ dilinde hem const hem de external linkage a bağlı yapmak istersek tanımda extern edilmelidir.

extern const int val = 5;

int main()
{
    return 0;
}

  • Ancak C dilinde syntax error C++ dilinde aşağıdaki durumda constant expression kullanmak zorunlu değildir.

int smart_code()
{
    return 1;
}

int c_val = smart_code();

int main()
{
    return 0;
}
*****************************   
AÇIKLAMA : static ömürlü değişkenler C dilinde de C++ dilinde de main çağırılmadan önce ilk değerlerini aldıkları için kod geçerlidir.
*****************************
int main () 
{ 
    int val = 5; 
    int val1 = 6; 
    const int* ptr = &val;   // int const* ptr = &val;

    *ptr = 12;               // syntax error. 
    ptr = &val1;             // legal
}
*****************************   
AÇIKLAMA : 
*****************************


FONKSİYON BİLDİRİMLERİNDE "const" KEYWORD


void smart_code(int* const val); 
  • Adresini aldığı nesne ya da nesneleri(eğer bu bir dizi ise) set etmeye yöneliktir. (mutater) nesneyi değiştirmeye yöneliktir.

  • Örneğin; fonksiyonun kendisini çağıran koda değer aktarmasının bir yöntemi olarak kullanılabilir. Böyle parametrelere output parameter denir. Call by reference tarzında bir fonksiyondur.


void smart_code(const int* val) 
*****************************   
AÇIKLAMA : okuma amaçlı bir nesneye erişim vardır. input parameter denir.

int strcmp(const char* p1, const char* p2);
char* strcpy(char* pdest, const char* psource)
*****************************
int main()
{
    const int val = 10;

    int* ptr = &val;       
/*****************************   
AÇIKLAMA : C++ dilinde val değişkeninin adresinin türü "const int*" 
ptr değişkeninin türü ise "int*" olur. C dilinde bu legal ancak yanlış bir kullanımdır. " const T* -> T* " dönüşümü error.
*****************************/                     
       
/*****************************   
AÇIKLAMA : böyle nesnelere fiziksel const denir.
val değişkeni hiç bir zaman değiştirilmeyecektir.
*****************************/                       
    *ptr = 5;      // syntax error değil ancak undefined behaviour
}
int main()
{
    int val = 5;

    int* const ptr = &val;

    *ptr = 6;                // legal
}
*****************************   
AÇIKLAMA : const pointer (top level const)
*****************************
int main()
{
    int val = 5;

    const int* ptr = &val;
    
    *ptr = 6;                // illegal
}
*****************************   
AÇIKLAMA : pointer to const(low level const)
ptr val değişkenini sadece okuma amaçlı kullancaktır.
*****************************
  • Yukarıdaki son 2 örnekte verilen const örnekleri pointer ile kullanımdır.

  • Bunu reference ile kullanmanın semantiği yoktur çünkü reference kendisi const olarak doğar.

int main()
{
    int val = 5;
    int& rval = val;

    rval = 6;            // legal
}
*****************************   
AÇIKLAMA :  int& const rval = val;    
*****************************
int main()
{
    int val = 5;
    const int& rval = val;

    rval = 6;        // illegal
}
*****************************   
AÇIKLAMA :  pointer to const karşılığıdır(low level const)   
*****************************

​void func(T& r)

setter

void func(const T& r)

getter


int main()
{
    int& rval = 10;
}
*****************************   
AÇIKLAMA :  syntax error  
*****************************
int smart_code();

int main() 
{ 
    int& rval = smart_code(); 
} 
*****************************   
AÇIKLAMA :  syntax error  
*****************************
int smart_code();

int main()
{
    const int& rval = smart_code(); 
}
*****************************   
AÇIKLAMA : reference burada bir sabit yerine geçer ona reference olur. Derleyici burada bir geçici nesne oluşturur. Referansı da o geçici nesneye bağlar.
*****************************
int smart_code(void);

int main() 
{ 
    const int val = 10;    // constant expression ile hayata geldi.
    
    const int val1 = smart_code();

    ++val;                    
    ++val1;                    
}
*****************************   
AÇIKLAMA : val ve val1 artırma işlemleri syntax error.
*****************************
int smart_code(void);

int main() 
{ 
    const int val = 10;        
    const int val1 = smart_code();

    int arr[val] = { 0 };    //legal
    int arr1[val1] = { 0 };  //illegal(expression must have a constant value)
}
*****************************   
AÇIKLAMA : 
*****************************

const correctness

  • const olması gereken her şey const olmalıdır. const anahtar sözcüğü fonksiyonun tanımında da bildiriminde de olmalıdır.

class SmartCode {
public :
    void func();
    void foo()const; // alttaki ile func. overloading durumundadır.
    void foo();
}

void SmartCode::foo()const
{
}

void SmartCode::foo(const SmartCode*)
{ 
}
*****************************   
AÇIKLAMA : Derleyici bunu yazar yani bu pointer'ın low level pointer olduğunu söyler
*****************************
class SmartCode {
private :
    int val;

public : 
    void func(); 
    void foo()const;         
}

void SmartCode::foo()const 
{
    val = 5;    // error
}

void SmartCode::func()  
{
    val = 5;   // legal 
}
class SmartCode { 
private : 
    int val; 

public : 
    void func(); 
    void foo()const;         
}

void SmartCode::foo()const 
{
    const SmartCode* ptr; 
    ptr->val = 5;        // hata olmasının nedeni buradaki durumdur 
}
*****************************   
AÇIKLAMA :
*****************************
class SmartCode { 
private : 
    int val;

public : 
    void func(); 
    void foo()const;         
}

void SmartCode::foo()const 
{ 
    SmartCode sc;
    sc.val = 5;        // geçerli 
}
  • Global bir fonksiyonda const anahtar sözcüğü kullanılmaz.

  • Static üye fonksiyonlarda da const anahtar sözcüğünün tanımda kullanılması syntax error durumudur.

void smart_code()const   
{}
*****************************   
AÇIKLAMA : global ise syntax error
*****************************
class SmartCode { 
private : 
    int mx; 

public : 
    void func(); 
    void foo()const;         
} 

int main()
{
    SmartCode sc;          //değeri değişebilir.
    const SmartCode m_sc;  //hayatı boyunca aynı değerde olacağını ifade eder

    m_sc.func();           //syntax error
}
*****************************   
AÇIKLAMA : m_sc hayatı boyunca aynı olmasını söyler. ancak func() nesneyi değiştirmeyi istemektedir. Burada m_sc nesnesinin adresini func()'un gizli parametre değişkenine gönderir.
func(SmartCode*) parametreli,  m_sc ise const SmartCode ve
const SmartCode* --> SmartCode* dönüşümüne derleyici hata verir.
*****************************
class SmartCode {
public :
    SmartCode(int);
    void func();
};

int main()
{
    const SmartCode sc(5);

    sc.func();   
}
*****************************   
AÇIKLAMA : func() const olmadığı için syntax error. 
*****************************
class SmartCode { 
private : 
    int mx; 

public : 
    void func(); 
    void foo()const;    
    void code()const;   
}

void SmartCode::func()
{
    foo();            // geçerli
}

void SmartCode::foo()const 
{ 
    func();        // geçersiz.(const SmartCode* -> SmartCode*) error.
    code();        // legal
}
*****************************   
AÇIKLAMA :
*****************************

CONSTEXPR KEYWORD


Şimdi de modern C++ ile dile eklenen ve oldukça kullanışlı olan constexpr anahtar sözcüğünü ele alalım. Derleme zamanında değeri hesaplanabilen ifadeleri ve nesneleri belirtmek için kullanılır. Programın derlenme sürecinde değeri bilinen sabit ifadeleri tanımlamak veya fonksiyonlara derleme zamanında değer atamak için kullanılır.

C++14 ve sonraki sürümlerde genişletilmiş ve iyileştirilmiştir. constexpr ifadesinin kullanımı, derleyiciye optimize edilmiş kod oluşturma şansı verir ve çalışma zamanında hesaplama gerektirmeyen daha hızlı programlar oluşturulmasını sağlar.

C++ dilinde; const nesnenin oluşturduğu ifadeyi constant expression gereken yerde kullanabilmek için ona ilk değer veren ifadenin de sabit ifade(constant expression) olması gerekmektedir. constexpr ile değişken tanımlandığında ilk değer veren ifadenin constant expression olması zorunludur. Nesnenin değiştirmesi gereken koşul const anahtar sözcüğü ile aynıdır.


int main()
{
    constexpr int val{10};
}
*****************************   
AÇIKLAMA : geçerli bir atamadır.
*****************************
int smart_code(void);

int main()
{
    constexpr int val = smart_code();
}
*****************************   
AÇIKLAMA : geçersiz bir atamadır.
*****************************
int main()
{
    int val = 5;

    const int* ptr = &val;

    constexpr const int* ptr = &val;   
}
*****************************   
AÇIKLAMA : syntax error. constexpr olduğu için sabit ifadesi ile ilk değer verilmelidir.
*****************************

constexpr function

  • Derleme zamanında değeri bilinen hesaplamaları yapmak için kullanılırlar. compile-time da hesaplanabilecek değerler için run-time da kaynak harcanmayacaktır.!!! Parametresine sabit ifadesi ile çağrı yapmak zorunlu değildir.

constexpr int square(int val)
{
    return val * val;
}

int main()
{
    int arr[square(10)] = { 0 };   // legal
}
*****************************   
AÇIKLAMA : bu fonksiyonun parametre(leri) constant expression olursa geri dönüş değeri compile time da hesaplanacaktır.
*****************************
constexpr int calc_square(int val) 
{ 
    return val * val; 
}

constexpr int calc_factorial(int val)
{
    return val < 2 ? 1 : val * fact(val - 1);
}

int main() 
{
    constexpr int f1 = calc_factorial(5); // legal
    constexpr int f2 = calc_square(f1);   // legal
}
constexpr int calc_factorial(int val) 
{ 
    return val < 2 ? 1 : val * calc_factorial(val - 1); 
}

constexpr bool calc_isprime(int val)
{    
    if ((val == 0) || (val == 1))
    {
        return false;
    }

    if (val % 2 == 0) 
    { 
        return val == 2; 
    }

    if (val % 3 == 0) 
    { 
        return val == 3; 
    }

    if (val % 5 == 0) 
    { 
        return val == 5; 
    }

    for (int idx = 7; idx * idx <= val; idx += 2)
    {
        if (val % idx == 0)
        {
            return false;
        }
    }

    return true;
}

int main()
{
    constexpr bool isprm = calc_isprime(17);
    constexpr bool isprm_fact = calc_isprime(calc_factorial(5) - 29);

    return 0;
}
constexpr int calc_factorial(int val)  
{  
    return val < 2 ? 1 : val * calc_factorial(val - 1);  
}

int main()
{
    int val = 10;

    calc_factorial(val);        
}
*****************************   
AÇIKLAMA : legal ancak artık run-time da hesaplanacaktır.
*****************************
constexpr int calc_square(int val) 
{ 
    return val * val; 
}

int main()
{
    int val;
    cout << "deger gir : " << " ";
    cin >> val;

    constexpr int res = calc_square(val);     
}
*****************************   
AÇIKLAMA : syntax error.
*****************************
constexpr int smart_code(int cnt)
{
    constexpr int arr[] = { 5, 7, 9, 6, 14, 18 };

    return arr[cnt];
}

int main()
{
    constexpr int val = smart_code(4);  
} 
*****************************   
AÇIKLAMA : compile-time da 14 olan 4.indisli(5.eleman) elemanı döndürür.
*****************************
constexpr int square(int val) 
{
    if constexpr (val > 0)
    {
        return val * val;
    }
    else
    {
        return -1;
    }
}

int main() 
{
    constexpr int num = 5;
    constexpr int result = square(num);

    return 0;
}
#include <chrono>  
#include <iostream> 

int main()  
{  
    constexpr auto time = 4h + 22min + 245s + 237ms + 98123us + 1264789ns;  
    std::cout << time.count();  
}
constexpr double operator"" _km(long double val) 
{ 
    return static_cast<double>(val * 100.); 
}

constexpr double operator"" _m(long double val)  
{  
    return static_cast<double>(val);  
} 
     
constexpr double operator"" _cm(long double val)  
{  
    return static_cast<double>(val / 100.);  
} 
     
constexpr double operator"" _mm(long double val)  
{  
    return static_cast<double>(val / 1000.);  
} 

int main()  
{       
    constexpr auto nm = 0.04_km + 1.16_m + 135.86_cm + 9857._mm; 
    
    cout << nm << endl; 
    cout << std::fixed << nm << endl; 
} 
#include <algorithm>
#include <iostream>
#include <iterator>
#include <array>

using namespace std;

int main()
{
    constexpr array v = { 1, 2, 3, 4, 4, 3, 8, 9, 10 };

    cout << "v: ";

    copy(v.cbegin(), v.cend(), ostream_iterator<int>(cout, " "));

    cout << '\n';

    for (const int tar : {3, 4, 5}) 
    {
        const int num_items = count(v.cbegin(), v.cend(), tar);

        cout << "number: " << tar << ", count: " << num_items << '\n';
    }

    int count_div4 = count_if(v.begin(), v.end(),[](int i){return i%4==0;});

    cout << "cnt : " << count_div4 << '\n';

    auto dis = [](auto first, auto last)
             {return count_if(first, last, [](auto){ return true;});};

    static_assert(dis(v.begin(), v.end()) == 10);
}
*****************************         
AÇIKLAMA :     
*****************************        
CEVAP : v: 1 2 3 4 4 3 7 8 9 10 
        number: 3, count: 2
        number: 4, count: 2
        number: 5, count: 0
        cnt : 3   
*****************************
template <int cnt> 
struct Factorial  
{ 
    constexpr static int val = cnt * Factorial<cnt - 1>::value; 
};

//explicit specialization 
template <>  
struct Factorial<0>  
{  
    constexpr static int val = 1;  
};

//partial specialization 
template <> 
struct Factorial<1>  
{ 
    constexpr static int val = 1; 
};

int main () 
{ 
    constexpr int calc = Factorial<4>::value;    
}
struct isPointer                                                       
{ 
    constexpr static bool value = false;          
};

//partial spec. 
template <typename T> 
struct isPointer<T *>               
{ 
    constexpr static bool value = true; 
}; 
      
template <typename T> 
struct isPointer<T[]>               
{ 
    constexpr static bool value = true; 
};

int main()  
{ 
    isPointer<int>::value;  
                                            
    cout << boolalpha << isPointer<int>::value << " ";                 
    cout << boolalpha << isPointer<double *>::value << " ";            
    cout << boolalpha << isPointer<double []>::value << endl;            
}
*****************************         
AÇIKLAMA :     
*****************************        
CEVAP : 0 1 1
*****************************

 
 
 

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...

 
 
 

Comments


bottom of page