Menu
Coddy logo textTech

std::function ve std::bind

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

Lambdalar güçlü olsa da, bazen farklı türlerdeki çağrılabilir nesneleri tek tip bir şekilde saklamanız veya mevcut fonksiyonları gerekli bir imzaya uyacak şekilde uyarlamanız gerekir. <functional> başlığındaki std::function ve std::bind bu sorunları çözer.

std::function, belirli bir imzayla eşleşen herhangi bir çağrılabilir nesneyi -fonksiyonlar, lambdalar veya fonksiyon nesneleri- tutabilen, türü silinmiş (type-erased) bir sarmalayıcıdır:

#include <iostream>
#include <functional>

int add(int a, int b) { return a + b; }

int main() {
    std::function<int(int, int)> operation;
    
    operation = add;                              // Normal fonksiyon
    std::cout << operation(3, 4) << "\n";        // 7
    
    operation = [](int a, int b) { return a * b; }; // Lambda
    std::cout << operation(3, 4) << "\n";        // 12
}

std::bind mevcut bir fonksiyonun bazı argümanlarını sabitleyerek yeni bir çağrılabilir (callable) oluşturur. Değişken kalacak argümanları işaretlemek için std::placeholders::_1, _2 vb. kullanın:

#include <iostream>
#include <functional>

void greet(const std::string& greeting, const std::string& name) {
    std::cout << greeting << ", " << name << "!\n";
}

int main() {
    using namespace std::placeholders;
    
    auto sayHello = std::bind(greet, "Hello", _1);
    sayHello("Alice");  // Hello, Alice!
    
    auto swapped = std::bind(greet, _2, _1);
    swapped("Bob", "Hi");  // Hi, Bob!
}

Bu araçlar, geri çağırmaları (callbacks) sınıf üyeleri olarak saklamak veya Strateji (Strategy) desenini uygulamak için OOP'de özellikle yararlıdır. Ancak, modern C++, daha iyi okunabilirlik ve performans için genellikle lambdaları std::bind yerine tercih eder - std::bind'ı esas olarak argümanları yeniden sıralamanız gerektiğinde veya eski kodlarla çalışırken kullanın.

challenge icon

Görev

Kolay

std::function ve std::bind'ın gücünü gösteren yapılandırılabilir bir hesap makinesi oluşturalım. Matematiksel işlemlerin çalışma zamanında saklanabildiği, değiştirilebildiği ve özelleştirilebildiği bir sistem oluşturacaksınız; bu araçların esnek geri çağırma (callback) yönetimini nasıl sağladığını göreceksiniz.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • MathOperations.h: İşlem kütüphaneniz olarak hizmet edecek bağımsız matematiksel fonksiyonlar koleksiyonu tanımlayın.

    Şu fonksiyonları oluşturun:

    • add(int a, int b) — toplamı döndürür
    • subtract(int a, int b) — farkı döndürür (a - b)
    • multiply(int a, int b) — çarpımı döndürür
    • power(int base, int exponent, int multiplier)multiplier * (base ^ exponent) değerini döndürür. Üssü hesaplamak için basit bir döngü kullanın (üslerin negatif olmadığını varsayın).
  • Calculator.h: İşlemleri dinamik olarak saklamak ve yürütmek için std::function kullanan bir Calculator sınıfı tanımlayın.

    Hesap makineniz şunlara sahip olmalıdır:

    • Mevcut ikili işlemi saklamak için özel (private) bir std::function<int(int, int)> üyesi
    • setOperation(std::function<int(int, int)> op) — mevcut işlemi ayarlar
    • calculate(int a, int b) — saklanan işlemi yürütür ve sonucu döndürür

    Gerekli başlık dosyalarını (<functional>) ekleyin ve MathOperations başlık dosyanızı dahil ettiğinizden emin olun.

  • main.cpp: Üç girdi okuyun:
    1. İşlem adı: add, subtract, multiply veya square
    2. Birinci sayı (tam sayı)
    3. İkinci sayı (tam sayı)

    Bir Calculator oluşturun ve işlem adına göre yapılandırın:

    • add, subtract ve multiply için: ilgili fonksiyonu doğrudan hesap makinesine atayın
    • square için: power fonksiyonunun her zaman 2 üssünü ve 1 çarpanını kullanan özelleştirilmiş bir versiyonunu oluşturmak için std::bind kullanın. Bağlanan fonksiyon, yalnızca birincisinin taban olarak kullanıldığı iki argüman kabul etmelidir (ikinci argüman bir yer tutucu kullanılarak yoksayılabilir).

    İşlemi ayarladıktan sonra, iki sayınızla calculate() fonksiyonunu çağırın ve şunu yazdırın:

    Result: [value]

    Ardından, hesap makinesine a + b + 100 döndüren bir lambda atayarak, aynı girdilerle çalıştırarak ve şunu yazdırarak işlem değiştirme işlemini gösterin:

    With bonus: [value]

Örneğin, add, 10 ve 5 girdileriyle:

Result: 15
With bonus: 115

multiply, 7 ve 3 girdileriyle:

Result: 21
With bonus: 110

square, 4 ve 0 girdileriyle:

Result: 16
With bonus: 104

Bu zorluk, std::function'ın farklı çağrılabilir türleri (normal fonksiyonlar, bağlanmış fonksiyonlar ve lambdalar) saklamak için nasıl tek tip bir yol sunduğunu, std::bind'ın ise mevcut fonksiyonları bazı argümanlarını sabitleyerek uyarlamanıza nasıl izin verdiğini gösterir.

Kopya kağıdı

<functional> başlığı, çağrılabilir nesnelerle esnek bir şekilde çalışmak için std::function ve std::bind sağlar.

std::function, belirli bir imzayla eşleşen herhangi bir çağrılabilir nesneyi saklayabilen, türü silinmiş (type-erased) bir sarmalayıcıdır:

#include <functional>

std::function<int(int, int)> operation;

operation = add;                              // Normal fonksiyon
operation = [](int a, int b) { return a * b; }; // Lambda

std::bind, mevcut bir fonksiyonun bazı argümanlarını sabitleyerek yeni bir çağrılabilir nesne oluşturur. Değişken argümanlar için std::placeholders::_1, _2 vb. kullanın:

#include <functional>

void greet(const std::string& greeting, const std::string& name) {
    std::cout << greeting << ", " << name << "!\n";
}

using namespace std::placeholders;

auto sayHello = std::bind(greet, "Hello", _1);  // İlk argümanı sabitle
sayHello("Alice");  // Hello, Alice!

auto swapped = std::bind(greet, _2, _1);  // Argümanları yeniden sırala
swapped("Bob", "Hi");  // Hi, Bob!

Bu araçlar, geri çağırmaları (callbacks) sınıf üyeleri olarak saklamak veya esnek geri çağırma sistemleri uygulamak için kullanışlıdır. Modern C++, daha iyi okunabilirlik için genellikle std::bind yerine lambdaları tercih eder.

Kendin dene

#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"

using namespace std;

int main() {
    // Girdileri oku
    string operation;
    int num1, num2;
    cin >> operation >> num1 >> num2;
    
    // Bir Calculator örneği oluştur
    Calculator calc;
    
    // TODO: Hesap makinesini işlem adına göre yapılandırın
    // "add", "subtract", "multiply" için: ilgili fonksiyonu doğrudan atayın
    // "square" için: power fonksiyonunun özelleştirilmiş bir versiyonunu oluşturmak için std::bind kullanın
    //   her zaman 2 üssünü ve 1 çarpanını kullanan
    //   İpucu: Taban argümanı için std::placeholders::_1 kullanın
    
    if (operation == "add") {
        // Kodunuz buraya
    } else if (operation == "subtract") {
        // Kodunuz buraya
    } else if (operation == "multiply") {
        // Kodunuz buraya
    } else if (operation == "square") {
        // Kodunuz buraya - power fonksiyonu ile std::bind kullanın
    }
    
    // TODO: calculate() fonksiyonunu çağırın ve sonucu yazdırın
    // Format: "Result: [value]"
    
    // TODO: İşlemlerin yer değiştirmesini gösterin
    // Hesap makinesine a + b + 100 döndüren bir lambda atayın
    // calculate() fonksiyonunu tekrar çağırın ve sonucu yazdırın
    // Format: "With bonus: [value]"
    
    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