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ı diğer 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&& şeklinde 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, isimlendirilmiş parametreler, bir rvalue geçilmiş olsa bile 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 iletim (perfect forwarding); nesneler oluşturması veya fonksiyonları çağırması gereken fabrika fonksiyonları, sarmalayıcı sınıflar ve her türlü jenerik kodu yazarken, çağıranın taşıma (move) veya kopyalamaya (copy) yönelik niyetini korumak için gereklidir.
Görev
KolayKendisine iletilen argümanların değer kategorisini (value category) koruyarak nesneler oluşturan ve mükemmel gönderimi (perfect forwarding) gösteren bir fabrika fonksiyonu inşa edelim. Argümanları bir sınıf kurucusuna 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 birWidgetsınıfı tanımlayın.Widgetsınıfınız birstd::stringname ve birintvalue saklamalıdır. İki adet kurucu (constructor) sağlayın:const std::string&(lvalue referansı) veintalan —"Widget constructed (copy): [name]"yazdırırstd::string&&(rvalue referansı) veintalan —"Widget constructed (move): [name]"yazdırır ve dizeyi taşır (move)
Ayrıca
[name]: [value]yazdıran birdisplay()metodu ekleyin.Factory.h: Mükemmel gönderim yapan fabrika fonksiyonunuzu oluşturun.İsim için bir gönderim referansı (forwarding reference) ve değer için normal bir
intkabul eden birmake_objectşablon fonksiyonu yazın. İsim argümanını, orijinal değer kategorisini koruyarakWidgetkurucusuna iletmek içinstd::forwardkullanın.Fonksiyon, oluşturulan
Widgetnesnesini değer olarak döndürmelidir.main.cpp: İki girdi okuyun:- Bir isim (string)
- Bir değer (integer)
İki widget oluşturarak mükemmel gönderimi gösterin:
- İlk olarak, ismi yerel bir değişkende saklayın ve
make_objectfonksiyonuna bir lvalue olarak geçirin - İkinci olarak, geçici bir dizeyi (aynı ismin
"_temp"ile birleştirilmiş hali) doğrudanmake_objectfonksiyonuna 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: 42Device ve 100 girdileriyle:
Widget constructed (copy): Device
Device: 100
Widget constructed (move): Device_temp
Device_temp: 100Buradaki temel nokta, make_object fonksiyonunuzun bir lvalue mu yoksa rvalue mu aldığını bilmemesidir; ancak std::forward kullanarak, Widget kurucusunu çağırırken bu bilgiyi korur. Bu, kurucu aşırı yükleme çözümlemesinin (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.
Kopya kağıdı
Mükemmel yönlendirme (perfect forwarding), şablon fonksiyonlarının argümanları orijinal değer kategorilerini (lvalue veya rvalue) koruyarak diğer fonksiyonlara aktarmasına olanak tanır.
Bu mekanizma, std::forward ile birleştirilmiş yönlendirme referanslarını (şablon bağlamında T&&) kullanır:
#include <utility>
template<typename T>
void wrapper(T&& arg) {
process(std::forward<T>(arg));
}Bir yönlendirme referansı hem lvalue hem de rvalue değerlere bağlanabilir. std::forward<T>, argümanı koşullu olarak orijinal türüne geri dönüştürür.
std::forward olmadan, bir fonksiyon içindeki isimlendirilmiş parametreler, bir rvalue geçilmiş olsa bile her zaman lvalue olarak değerlendirilir. Bu durum, taşıma semantiğinin (move semantics) doğru çalışmasını engeller.
Değer kategorisi korumasını gösteren örnek:
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 n = 10;
wrapper(n); // process(int&) çağrılır - lvalue korunur
wrapper(20); // process(int&&) çağrılır - rvalue korunurMükemmel yönlendirme; fabrika fonksiyonları, sarmalayıcı sınıflar ve nesneler oluşturan veya fonksiyonları çağıran genel (generic) kodlar için, çağıranın taşıma veya kopyalama konusundaki niyetini korumak açısından esastır.
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;
// TODO: name değişkenini bir lvalue olarak geçirerek ilk widget'ı oluşturun
// İsmi yerel bir değişkende saklayın ve make_object fonksiyonuna geçirin
// TODO: İlk widget üzerinde display() fonksiyonunu çağırın
// TODO: Geçici bir string'i rvalue olarak geçirerek ikinci widget'ı oluşturun
// (name + "_temp") ifadesini doğrudan make_object fonksiyonuna geçirin
// TODO: İkinci widget üzerinde display() fonksiyonunu çağırın
return 0;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner12Modern C++ Özellikleri
Taşıma Semantiği ve R-değerleriMükemmel YönlendirmeDerinlemesine Lambda İfadeleristd::function ve std::bindconstexpr ve constevalYapılandırılmış Bağlamalaroptional, variant, any