Menu
CoddyTech

Penerusan Sempurna

Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 84 dari 104.

Saat menulis fungsi template yang menerima argumen dan meneruskannya ke fungsi lain, muncul masalah: bagaimana kamu mempertahankan apakah argumen aslinya berupa lvalue atau rvalue? Perfect forwarding mengatasi masalah ini dengan meneruskan argumen persis seperti saat diterima, dengan mempertahankan kategori nilainya.

Bahan utamanya adalah referensi penerusan (ditulis sebagai T&& dalam konteks templat) dan std::forward. Referensi penerusan dapat terikat pada lvalue maupun rvalue, dan std::forward secara kondisional meng-cast argumen kembali ke tipe aslinya:

#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);       // Memanggil process(int&), lvalue tetap terjaga
    wrapper(20);      // Memanggil process(int&&), rvalue tetap terjaga
}

Tanpa std::forward, parameter bernama selalu berupa lvalue di dalam fungsi, meskipun yang diteruskan adalah rvalue. Hal ini akan membuat semantik pemindahan tidak berfungsi dengan benar saat meneruskan parameter ke konstruktor atau fungsi lain.

Penerusan sempurna sangat penting untuk menulis fungsi factory, wrapper class, dan kode generik apa pun yang perlu membuat object atau memanggil fungsi sambil mempertahankan maksud pemanggil terkait operasi move dan copy.

challenge icon

Tantangan

Mudah

Mari buat fungsi factory yang menunjukkan perfect forwarding dengan membangun object sambil mempertahankan kategori nilai argumen yang diberikan kepadanya. Kamu akan membuat fungsi generik make_object yang meneruskan argumen ke constructor class, memastikan bahwa lvalue disalin dan rvalue dipindahkan.

Kamu akan mengatur kodemu dalam tiga file:

  • Widget.h: Definisikan class Widget yang mencatat cara menerima datanya.

    Widget harus menyimpan nama bertipe std::string dan nilai bertipe int. Buat dua constructor:

    • Satu yang menerima const std::string& (referensi lvalue) dan int: mencetak Widget constructed (copy): [name]
    • Satu yang menerima std::string&& (referensi rvalue) dan int: mencetak Widget constructed (move): [name] dan memindahkan string

    Tambahkan juga method display() yang mencetak [name]: [value].

  • Factory.h: Buat fungsi factory perfect forwarding.

    Tulis fungsi template make_object yang menerima forwarding reference untuk nama dan int biasa untuk nilai. Gunakan std::forward untuk meneruskan argumen nama ke constructor Widget sambil mempertahankan kategori nilai aslinya.

    Fungsi tersebut harus mengembalikan Widget yang telah dibuat berdasarkan nilai.

  • main.cpp: Baca dua input:
    1. Sebuah nama (string)
    2. Sebuah nilai (bilangan bulat)

    Tunjukkan perfect forwarding dengan membuat dua widget:

    1. Pertama, simpan nama dalam variabel lokal dan berikan ke make_object sebagai lvalue
    2. Kedua, berikan string sementara (nama yang sama digabungkan dengan "_temp") langsung ke make_object sebagai rvalue

    Setelah membuat setiap widget, panggil method display()-nya.

Sebagai contoh, dengan input Gadget dan 42:

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

Dengan input Device dan 100:

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

Inti utamanya adalah fungsi make_object tidak mengetahui apakah ia menerima lvalue atau rvalue, tetapi dengan menggunakan std::forward, fungsi tersebut mempertahankan informasi itu saat memanggil constructor Widget. Dengan demikian, resolusi overload constructor dapat memilih versi yang sesuai, sehingga perpindahan dapat dilakukan secara efisien untuk nilai sementara, sementara variabel bernama disalin dengan aman.

Coba sendiri

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

using namespace std;

int main() {
    // Baca input
    string name;
    int value;
    cin >> name >> value;
    
    // TODO: Buat widget pertama dengan meneruskan name sebagai lvalue
    // Simpan name dalam variabel lokal, lalu teruskan ke make_object
    
    
    // TODO: Panggil display() pada widget pertama
    
    
    // TODO: Buat widget kedua dengan meneruskan string sementara sebagai rvalue
    // Teruskan (name + "_temp") langsung ke make_object
    
    
    // TODO: Panggil display() pada widget kedua
    
    
    return 0;
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Object

Latihan sendiri: Compiler C++ online