Menu
Coddy logo textTech

Mükemmel Yönlendirme

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 84 / 104.

Argümanları kabul eden ve onları başka fonksiyonlara ileten şablon fonksiyonları yazarken bir sorun ortaya çıkar: Orijinal argümanın bir lvalue mu yoksa rvalue mu olduğunu nasıl korursunuz? Perfect forwarding, argümanları tam olarak alındıkları gibi ileterek ve değer kategorilerini koruyarak bu sorunu çözer.

Temel bileşenler yönlendirme referansları (bir şablon bağlamında T&& olarak yazılır) ve std::forward'dır. Bir yönlendirme referansı hem lvalue'lara hem de rvalue'lara bağlanabilir ve std::forward argümanı koşullu olarak orijinal türüne geri dönüştürür:

#include <iostream>
#include <utility>

void process(int& x) { std::cout << "lvalue: " << x << "\n"; }
void process(int&& x) { std::cout << "rvalue: " << x << "\n"; }

template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));
}

int main() {
    int n = 10;
    wrapper(n);       // process(int&) çağrılır - lvalue korunur
    wrapper(20);      // process(int&&) çağrılır - rvalue korunur
}

std::forward olmadan, bir rvalue geçilmiş olsa bile, adlandırılmış parametreler fonksiyon içinde her zaman lvalue'dur. Bu, kuruculara veya diğer fonksiyonlara iletim yaparken taşıma semantiğinin (move semantics) doğru çalışmasını engeller.

Mükemmel yönlendirme (perfect forwarding); fabrika fonksiyonları, sarmalayıcı sınıflar ve nesneler oluşturması veya fonksiyonları çağırması gereken herhangi bir genel (generic) kod yazarken, çağıranın taşıma (move) veya kopyalama (copy) konusundaki niyetini korumak için gereklidir.

challenge icon

Görev

Kolay

Kendisine iletilen argümanların değer kategorisini (value category) koruyarak nesneler oluşturan ve mükemmel iletimi (perfect forwarding) gösteren bir fabrika fonksiyonu inşa edelim. Argümanları bir sınıf yapıcısına (constructor) ileten, lvalue'ların kopyalanmasını ve rvalue'ların taşınmasını sağlayan genel bir make_object fonksiyonu oluşturacaksınız.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • Widget.h: Verilerini nasıl aldığını takip eden bir Widget sınıfı tanımlayın.

    Widget sınıfınız bir std::string name ve bir int value saklamalıdır. İki adet yapıcı (constructor) sağlayın:

    • const std::string& (lvalue referansı) ve int alan — Widget constructed (copy): [name] yazdırır
    • std::string&& (rvalue referansı) ve int alan — Widget constructed (move): [name] yazdırır ve dizeyi taşır (move)

    Ayrıca [name]: [value] yazdıran bir display() metodu ekleyin.

  • Factory.h: Mükemmel iletim yapan fabrika fonksiyonunuzu oluşturun.

    İsim için bir iletim referansı (forwarding reference) ve değer için normal bir int kabul eden bir make_object şablon fonksiyonu yazın. İsim argümanını, orijinal değer kategorisini koruyarak Widget yapıcısına iletmek için std::forward kullanın.

    Fonksiyon, oluşturulan Widget nesnesini değer olarak döndürmelidir.

  • main.cpp: İki girdi okuyun:
    1. Bir isim (string)
    2. Bir değer (integer)

    İki widget oluşturarak mükemmel iletimi gösterin:

    1. İlk olarak, ismi yerel bir değişkende saklayın ve make_object fonksiyonuna bir lvalue olarak geçirin
    2. İkinci olarak, geçici bir dizeyi (aynı ismin "_temp" ile birleştirilmiş hali) doğrudan make_object fonksiyonuna bir rvalue olarak geçirin

    Her widget'ı oluşturduktan sonra display() metodunu çağırın.

Örneğin, Gadget ve 42 girdileriyle:

Widget constructed (copy): Gadget
Gadget: 42
Widget constructed (move): Gadget_temp
Gadget_temp: 42

Device ve 100 girdileriyle:

Widget constructed (copy): Device
Device: 100
Widget constructed (move): Device_temp
Device_temp: 100

Buradaki temel nokta, make_object fonksiyonunuzun bir lvalue mu yoksa rvalue mu aldığını bilmemesidir; ancak std::forward kullanarak, Widget yapıcısını çağırırken bu bilgiyi korur. Bu, yapıcı aşırı yükleme çözünürlüğünün (overload resolution) uygun sürümü seçmesine olanak tanıyarak, isimlendirilmiş değişkenleri güvenli bir şekilde kopyalarken geçici nesneler için verimli taşımalar (moves) sağlar.

Kendin dene

#include <iostream>
#include <string>
#include "Widget.h"
#include "Factory.h"

using namespace std;

int main() {
    // Girdiyi oku
    string name;
    int value;
    cin >> name >> value;
    
    // YAPILACAK: İsmi bir lvalue olarak geçirerek ilk widget'ı oluşturun
    // İsmi yerel bir değişkende saklayın ve make_object'e geçirin
    
    
    // YAPILACAK: İlk widget üzerinde display() fonksiyonunu çağırın
    
    
    // YAPILACAK: Geçici bir dizgiyi (string) rvalue olarak geçirerek ikinci widget'ı oluşturun
    // (name + "_temp") ifadesini doğrudan make_object'e geçirin
    
    
    // YAPILACAK: İkinci widget üzerinde display() fonksiyonunu çağırın
    
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler