C++ 20

Paylaş

C++ 20 ile birlikte gelen coroutines, modules, Constraints and concepts, format, jthread, thread yönetimi özellikleri ve diğer özelliklerle ilgili bilgiler örneklerle yer alıyor.

C++ 20 nedir?

C++ 11 ile birlikte Modern C++ olarak adlandırılan C++ diline bir çok yeni özelliğin eklendiği sürümdür.

C++ dilinin C++20 sürümüyle dile çeşitli eklemelerin yanında modüler olması, konsept ile daha anlaşılır geliştirme yapmayı gibi özellikler gelmiştir.

Paralel proglamada kullanılan semafor-semaphore, bariyer-barrier ve latch kapılarının yanında jthread ve ostream gibi özelliklerle daha kolay Thread yönetimi sağlanmıştır.

C++ 20 ile gelen yeni özelliklere bakmadan önce C++ 17 yazıma bakmalısın.

Nasıl çalıştırılır?

C++ 20 ile gelen özellikler bir çok derleyici (GCC 8, Clang 6, MSVC 19.25) tarafından kullanıma hazır hale getirilmiştir.

C++ 20 özelliklerinin kullanmak için derleyici parametresine (flags) -std=c++2a veya -std=c++20 eklemek yeterli olacaktır.

1. Feature test macros

C++ özelliklerinin derleyici tarafından desteklenip desteklenmediğini makrolar üzerinden tespit etmeyi sağlar.

#include <iostream>
#include <version>

int main(){
    if constexpr (__cpp_constexpr >= 200704L) {
        std::cout << "C++20 constexpr özelliğini destekliyor." << '\n';
    } else {
        std::cout << "C++20 constexpr özelliğini desteklemiyor." << '\n';
    }
    return 0;
}

C++20 ile birlikte eklenen özellik __cpp ile başlayan çeşitli makrolar tanımlayarak derleyicinin özelliği desteğini kontrol etmeyi sağlar.

Tüm tanımlı makrolara şuradan ulaşabilirsiniz.

2. Üçlü karşılaştırma – Three-way comparison

Opertatör iki değeri karşılaştırarak sonucun büyük, küçük veya eşit olduğunu std::strong_ordering, std::partial_ordering, std::weak_ordering türünden döndürür.

#include <iostream>

int main() {
    auto sonuc = 50 <=> 40;

    if (sonuc == std::strong_ordering::less) {
        std::cout << "50 küçüktür";
    }
    else if (sonuc == std::strong_ordering::greater) {
        std::cout << "50 büyüktür";
    }
    else {
        std::cout << "eşittir";
    }

    return 0;
}

Özellik operatör olduğu için sınıf içerisinde opratör olarak tanımlanarak sınıflar arasında karşılaştırma için kullanılabilir.

3. Designated initializers

C programlama dilinde struct, union türlerindeki elemanlara değer atamak için kullanılan özellik C++ diline eklenmiştir.

#include <iostream>

struct Person {
    std::string firstName;
    std::string lastName;
};

int main() {
    Person yusuf = {.firstName = "Yusuf", .lastName = "Sezer"};
    std::cout << yusuf.firstName << " " << yusuf.lastName << '\n';
    return 0;
}

4. Init-statements ve initializers in range-for

C++ 11 ile birlikte gelen range-based döngüler özelliğine döngü önüne ifade eklemeyi sağlar.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> sayilar{5, 3, 8, 9, 11};

    for (/* init-statement */ auto it = sayilar.begin(); auto mevcut : sayilar) {
        std::cout << mevcut << '\n';
    }

    return 0;
}

5. char8_t

C++ ile uygulama geliştirme sırasında Unicode/UTF-8 özelliğini eklenere geniş dil desteğini sağlanmıştır.

#include <iostream>
#include <vector>

int main() {
    std::u8string my_name = u8"Yusuf Sefa Sezer";

    for (char8_t harf : my_name) {
        std::printf("%c", harf);
    }

    return 0;
}

6. likely, unlikely nitelikleri

Nitelikler derleyiciye optimizasyon ipucu vererek daha performanslı çalışmasını sağlar.

constexpr long long fact(long long n) noexcept {
if (n > 1) [[likely]]
    return n * fact(n - 1);
else [[unlikely]]
    return 1;
}

Yukarıdaki kod parçası derleyiciye [[likely]] niteliği ile kodun büyük olasılıkla belirtilen bloğu kullanacağını, [[unlikely]] ile ise daha az olasılıkla diğer kod bloğunu kullanılacağı bilgisini verir.

7. consteval

consteval anahtar kelimesi fonksiyon sonucunun derleme sırasında hesaplanmasını sağlar.

#include<iostream>

consteval int hesapla() {
    return 10 + 10;
}

int main(){
    int sonuc = hesapla();
    std::cout << sonuc << '\n';
    return 0;
}

8. constinit

constinit anahtar kelimesi değişken değerinin derleme sırasında hesaplanmasını sağlar.

#include<iostream>

constinit int sonuc = 20 + 10;

int main(){
    std::cout << ::sonuc << '\n';
    return 0;
}

9. coroutines

C++ 20 ile birlikte gelen coroutines özelliği asenkron veya yarıda kesilebilen işlemler yapmayı sağlar.

Özelliğin kullanımı ve yönetiminide co_await, co_yield, co_return anahtar kelimeleri kullanılır.

#include <iostream>
#include <coroutine>
#include <thread>

struct HelloCoroutine {
  struct HelloPromise {
    HelloCoroutine get_return_object() { return std::coroutine_handle<HelloPromise>::from_promise(*this); }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void return_value(int value) { std::cout << "sonuç " << value << "\n"; }
    void unhandled_exception() {}
  };

  using promise_type = HelloPromise;
  HelloCoroutine(std::coroutine_handle<HelloPromise> h) : handle(h) {}
  std::coroutine_handle<HelloPromise> handle;
};

struct LifetimeInspector {
  LifetimeInspector(std::string s) : s(s) {
    std::cout << "Başladı: " << s << std::endl;
  }
  ~LifetimeInspector() {
    std::cout << "Bitti: " << s << std::endl;
  }
  std::string s;
};

HelloCoroutine count_to_ten() {
  LifetimeInspector l("count_to_ten");
  for (int i = 0; i < 10; ++i) {
    if (i == 5){
      co_await std::suspend_always{};
    }
    std::cout << i << std::endl;
  }
  co_return 42;
}

int main() {
  LifetimeInspector i("main");
  HelloCoroutine mycoro = count_to_ten();

  std::this_thread::sleep_for(std::chrono::seconds(3)); // 3sn beklet
  std::cout << "devam ettir" << std::endl;
  mycoro.handle.resume(); // döngüyü kaldığı yerden devam ettir.

  std::cout << "temizle" << std::endl;
  mycoro.handle.destroy();
}

Örnekte co_await ile belleğin heap bölgesinde bekletilen işlem resume ile devam ettirilmiştir.

Böylece döngüdeki tüm işlemler tek seferde yapılmak yerine belirli yere kadar yapılıp daha sonra kalınan yerden devam etmiştir.

Özellik soket programalama, dosya işlemleri gibi uzun süren işlemleri daha etkili ve performanslı yönetmeyi sağlar.

10. modules

C++ 20 ile birlikte gelen en önemli özelliklerden birisi olan modules, namespaces özelliğine benzer şekilde geliştirmeyi daha düzenli yapmayı sağlar.

Namespaces den farkı export anahtar kelimesiyle sadece istenilen kısımların dışarıdan kullanılabilir olmasını sağlayarak derleme süresi ve bağımlılık yönetimini düzenli hale getirmesidir.

Aşağıdaki kodu merhaba.cpp olarak kayıt edin.

export module merhaba; // modül tanımı

import <iostream>; // import tanıtım

export void yazdir(){ // export tanıtım
    std::cout << "Merhaba C++ modules!\n";
}

Aşağıdaki kodu main.cpp olarak kayıt edin.

import merhaba; // import tanıtım
 
int main(){
    yazdir();
}

Derleme öncesi varsa kullanılan C++ modüllerinin derlenir.

g++ -fmodules-ts -x c++-system-header iostream

Kullanılan modüller derlendikten sonra proje derlenir.

g++ -fmodules-ts merhaba.cpp main.cpp

NOT: Özelliğin kullanımı derleyiciye göre değişiklik gösterir.

11. Constraints and concepts

C++ içerisinde yer alan özelliklerin belirli kuralı olmadığı ve orta seviyeli bir programalam dili olduğundan undefined behaviour sıklıkla karşılaşılır.

Bu sorunun azaltmak için geliştirilen concepts veya kavramlar class template, function template ve metotlar için kural ve sınır belirlemeyi sağlar.

Tanım için concept anahtar kelimesi kullanılır.

template <typename T>
concept Number = requires(T a, T b) { a + b; a - b; a * b; a / b; };

Tanımlanan kavram (concept) template içerisinde kullanılır.

template <Number N>
auto topla(const N &a, const N &b){ return a + b; }

Oluşturulan fonksiyon kavram tarafından belirtilen matematiksel işlemleri yapan türler tarafından kullanılır.

int a = 10;
int b = 20;
auto c = topla(a, b);
std::cout << c;

Kavram içerisinde tanımlanan işlemleri yapmaya izin vermeyen türler tarafından yapılmak istendiğine derleme sırasında hata verecektir.

std::string aa = "10";
std::string bb = "20";
auto cc = topla(aa, bb);   // error: string is not a Number
return 0;
#include <iostream>

template <typename T>
concept Number = requires(T a, T b) { a + b; a - b; a * b; a / b; };

template <Number N>
auto topla(const N &a, const N &b) { return a + b; }

int main() {
    int a = 10;
    int b = 20;
    auto c = topla(a, b);
    std::cout << c;

    std::string aa = "10";
    std::string bb = "20";
    // auto cc = topla(aa, bb); // error: no matching function for call to
    return 0;
}

Aşağıdaki başka bir kavram(concept) örneği yer almaktadır.

template<typename T>
concept Printable = requires(T a) {
    { std::cout << a } -> std::same_as<std::ostream&>;
};

C++ 20 ile gelen kavram(concept) özelliği sayesinde template yapıları daha düzenli ve öngörülebilir hale gelmiştir.

Özellik concepts başlığında same_as gibi çeşitli fonksiyonlar sayesinde kavramlara daha fazla kısıt koymayı sağlar.

12. bit_cast

C++ 20 bit başlık dosyasıyla gelen özellik dönüşüm işlemini bit düzeyinde yapmayı sağlar.

#include <bit>
#include <iostream>

int main() {
    int a = 10;
    float b = std::bit_cast<float>(a);
    std::cout << b << '\n';
    return 0;
}

Dönüşüm işlemi tür düzeyi yerine bit düzeyinde yapıldığından dolayı sonuç tanımsız davranış (undefined behavior) verebilir.

13. source_location

C++ source_location ile birlikte gelen özellik ön tanımlı makrolar(__FILE__, __LINE__) gibi kaynak dosya adı, satır-sütun numarası ve diğer bilgilere erişimi sağlar.

#include <iostream>
#include <string_view>
#include <source_location>

void log(const std::string_view message,
         const std::source_location location =
             std::source_location::current()) {
    std::clog << "file: "
              << location.file_name() << "("
              << location.line() << ":"
              << location.column() << ") `"
              << location.function_name() << "`: "
              << message << '\n';
}

template <typename T>
void fun(T x) {
    log(x);
}

int main() {
    log("Hello world!");
    fun("Hello C++20!");
}

14. format

C programlama dilinde formatlı çıktı almak için kullanılan printf fonksiyonun C++ dilindeki standart ve güvenli halidir.

#include<iostream>
#include<format>

int main() {
  std::cout << std::format("Merhaba {} {} {} \n", "Yusuf", "Sezer", 1453);
  std::cout << std::format("Merhaba {2} {0} {1} \n", "Yusuf", "Sezer", 1453);
  return 0;
}

15. Matematiksel sabitler

Matematiksel hesaplamalarda kullanılan çeşitli sabit tanımları numbers başlık dosyası ile C++ standardına eklenmiştir.

#include <iostream>
#include <numbers>

int main() {
  std::cout << std::numbers::e << '\n';
  std::cout << std::numbers::log10e << '\n';
  std::cout << std::numbers::pi << '\n';
  std::cout << std::numbers::inv_pi << '\n';
  std::cout << std::numbers::inv_sqrtpi << '\n';
  std::cout << std::numbers::ln2 << '\n';
  std::cout << std::numbers::ln10 << '\n';
  std::cout << std::numbers::sqrt2 << '\n';
  std::cout << std::numbers::sqrt3 << '\n';
  std::cout << std::numbers::inv_sqrt3 << '\n';
  std::cout << std::numbers::egamma << '\n';
  return 0;
}

16. Ranges

Sıralama, arama ve döngülerde sıklıkla kullanılan başlangıç ve bitiş gibi aralıkları ifade etmek için ranges başlık dosyası gelen bir özelliktir.

#include <iostream>
#include <vector>
#include <ranges>

int main() {
  std::vector<int> sayilar{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  for (int sayi : sayilar | std::views::filter([](int n){ return n % 2 == 0; })) {
    std::cout << sayi << " ";
  }

  std::cout << std::endl;
  return 0;
}

Kütüphanenin sağladığı çeşitli metotlar (std::views::filter, std::views::take, std::views::all , std::views::keys vb.) ile aralık belirlenerek daha anlaşılır kod yazmayı sağlar.

17. version

C++ 20 ile birlikte C++ sürümlerine ait özellikler için çeşit makroların tanımlandığı version başlığıdır.

Başlık dosyasında tanımlı değerler kullanılan derleyici sürümüne göre işlem yapmayı sağlar.

#include <iostream>
#include <version>

#if __cpp_impl_three_way_comparison >= 201907
    // Üçlü karşılaştırma operatörü (<=>
    // C++20 ile geldiğinde bu kod bloğu çalışır
    bool compare(int a, int b) {
        return a <=> b == 0;
    }
#else
    // C++20 öncesinde bu kod bloğu çalışır
    bool compare(int a, int b) {
        return a == b;
    }
#endif

int main() {
    std::cout << std::boolalpha << compare(5, 5) << '\n';
    std::cout << std::boolalpha << compare(5, 10) << '\n';
    return 0;
}

18. jthread

C++ 11 ile birlikte C++ standartı olan Thread özelliğine ek olarak işlev bittiğinde sonlandırmayı sağlar.

#include <iostream>
#include <thread>

void yazdir() {
  std::cout << "Thread id: " << std::this_thread::get_id() << '\n';
  std::cout << "Yusuf Sezer" << '\n';
}

int main() {
  std::jthread alt_islem(yazdir);
  std::cout << "Thread id: " << std::this_thread::get_id() << '\n';
  return 0;
}

C++ Thread yazımda yer alan std::thread yerine std::jthread kullanılmış halidir.

19. semaphore

Thread senkronizasyonunda kullanılan semaphore yöntemi semaphore başlık dosyası ile C++ standardı olarak dile eklenmiştir.

#include <chrono>
#include <iostream>
#include <semaphore>
#include <thread>
 
// semaphore tanımla
std::binary_semaphore smphSignalMainToThread{0}, smphSignalThreadToMain{0};
 
void ThreadProc() {
    // smphSignalMainToThread Semaphore'u kilitle
    smphSignalMainToThread.acquire();
 
    std::cout << "[thread] Got the signal\n";
 
    // 3 sn görev yap.
    using namespace std::literals;
    std::this_thread::sleep_for(3s);
 
    std::cout << "[thread] Send the signal\n";
 
    // smphSignalThreadToMain Semaphore'u serbest bırak
    smphSignalThreadToMain.release();
}
 
int main() {
    // thread oluştur
    std::thread thrWorker(ThreadProc);
 
    std::cout << "[main] Send the signal\n";
 
    // smphSignalMainToThread Semaphore'u serbest bırak
    smphSignalMainToThread.release();
 
    // smphSignalMainToThread Semaphore'u kilitle
    smphSignalThreadToMain.acquire();
 
    std::cout << "[main] Got the signal\n";
    thrWorker.join();
}

20. latch

Thread senkronizasyonunda kullanılan latch yöntemi latch başlık dosyası ile C++ standardı olarak dile eklenmiştir.

#include <iostream>
#include <thread>
#include <latch>

void gorev(std::latch& latch) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "İş parçacığı tamamlandı." << '\n';
    latch.count_down();
}

int main() {
    const int threadSayisi = 3;

    // 3 iş parçacığını bekleyecek bir latch oluştur
    std::latch latch(threadSayisi);

    for (int i = 0; i < threadSayisi; ++i) {
        std::thread(gorev, std::ref(latch)).detach();
    }

    // Latch tamamlanana kadar bekle
    latch.wait();
    std::cout << "Tüm iş parçacıkları tamamlandı." << '\n';

    return 0;
}

20. barrier

Thread senkronizasyonunda kullanılan barrier yöntemi barrier başlık dosyası ile C++ standardı olarak dile eklenmiştir.

#include <barrier>
#include <iostream>
#include <thread>
#include <vector>
 
int main() {
    const auto isimler = {"Yusuf", "Sefa", "Sezer"};
 
    auto gorev_tamamla = []() noexcept     {
        static auto adim =
            "... başlatıldı\n"
            "Bitiriliyor...\n";
        std::cout << adim;
        adim = "... tamamlandı\n";
    };
 
    std::barrier sync_point(std::ssize(isimler), gorev_tamamla);
 
    auto gorev = [&](std::string isim)
    {
        std::string is = "  " + isim + " başladı\n";
        std::cout << is;
        sync_point.arrive_and_wait();
 
        is = "  " + isim + " tamam\n";
        std::cout << is;
        sync_point.arrive_and_wait();
    };
 
    std::cout << "Başlatılıyor...\n";
    std::vector<std::jthread> threads;
    threads.reserve(std::size(isimler));
    for (auto const& worker : isimler)
        threads.emplace_back(gorev, worker);
}

21. stop_token

C++ 20 ile birlikte gelen jthread ile oluşturulan iş parçacıklarını yönetmek için stop_token başlık dosyası ile C++ standardı olarak dile eklenmiştir.

#include <iostream>
#include <thread>
#include <chrono>
#include <stop_token>

void gorev(std::stop_token stoken) {
  while (!stoken.stop_requested()) {
    std::cout << "Görev çalışıyor...\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  std::cout << "Görev tamamlandı.\n";
}

int main() {
  std::jthread t(gorev);
  std::this_thread::sleep_for(std::chrono::seconds(5));
  return 0;
}

Yukarıdaki örnekte iş parçacığı jthread ile başlatılmış ve std::this_thread::sleep_for ile main iş parçacığı 5 sn bekletilmiştir. Ana iş parçacığı(main) görevi tamamladığında thread tarafından arka planda request_stop ve join çalıştırılarak işin bitirilmesi sağlanmıştır.

22. span

Bellekte sıralı olarak yer alan(dizi gibi) elemanların bellek alanını ifade etmek için span başlık dosyası ile C++ standartı olarak eklenmiştir.

#include <iostream>
#include <vector>
#include <span>

int main() {
  std::vector<int> sayilar{1, 2, 3, 4, 5};
  std::span<int> span(sayilar.data(), sayilar.size());

  // span içindeki verilere erişim
  for (int i : span)
  {
    std::cout << i << " ";
  }
  std::cout << '\n';

  return 0;
}

22. syncstream

C++ 20 ile gelen özellik Thread veya çoklu iş parçacığı ile geliştirilen uygulamalardaki çıktı senkronizasyonu sağlayarak race conditions ve undefined behaviour oluşmasını önler.

#include <iostream>
#include <sstream>
#include <thread>
#include <syncstream>

void yazdir(std::osyncstream& os, int id) {
    os << "Thread " << id << '\n';
}

int main() {
    std::ostringstream stringStream;
    std::osyncstream syncStream(stringStream);

    std::thread t1(yazdir, std::ref(syncStream), 1);
    std::thread t2(yazdir, std::ref(syncStream), 2);

    t1.join();
    t2.join();

    std::cout << stringStream.str();

    return 0;
}

23. Diğer

C++ 20, Önceki Modern C++ sürümleri C++ 11, C++ 14 ve C++ 17 göre daha fazla özelliğin eklendiği sürümdür.

C içerisinde yer alan printf, macrolar (__FILE__ vb) gibi özellikler C++ standartı olarak eklenmiştir.

Birden çok dili destekleyen uygulamalar için UTF-8 desteği (char8_t) genişletilmiştir.

C++ 20 sürümünde gelen birçok yeniliğin yanında Thread, Asenkron ve Paralelel programalam gibi yaklaşımlarda senkronizasyon ve race condition gibi durumlar için kullanılan yöntemler standart olarak eklenmiştir.

Eklenen yeni özelliklerin yanında önceki Modern C++ sürümlerinde gelen chrono gibi başlıklara çeşitli eklemeler yapılmıştır.

Yeni güncellemeler ve dil hakkında detaylı bilgi için isocpp.org adresine bakabilirsiniz.

Hayırlı günler dilerim.


Bunlarda ilgini çekebilir


Destek almak için tıklayın.