top of page

C++ Dilinde Lambda İfadeleri

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Oct 1, 2023
  • 7 min read

Updated: Apr 21

C++11 standartları ile dile eklenen lambda ifadeleri; isimsiz fonksiyon oluşturma, fonksiyon nesneleri ve/veya işaretçilerinde kullanılmaktadırlar. Yani fonksiyon paradigmalarında ve matematiksel ifadelerde yaygın şekilde kullanılmaktadır.

Bir lambda ifadesi derleyicilere sınıfın kodunu yazdırarak sınıf türünden bir geçici nesneye karşılık gelmektedir.

[], () ve {} token lambda ifadesi oluşturmak için kullanılmaktadır. Aynı zamanda bu üç token bir arada geçerli bir ifadesidir.

#include <iostream>

class SmartLambda {
public :
    void operator()() const {}
};

int main() 
{
    [](){}();

    SmartLambda{}();
    
    return 0;
}
*****************************
AÇIKLAMA: [](){} ifadesi ile bir lambda oluşturuluyor ve () ile çağrı yapılmaktadır. 
Sınıf nesnesi türünden de bir nesne oluşturuluyor, () operatorü de overload edildiği için de sınıf nesnesinin çağrısı geçerlidir.
*****************************
CEVAP:
*****************************
#include <iostream>

int main() 
{ 
    std::cout << [](int val){ return val * val; }(10) << std::endl; 
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 100
*****************************
#include <iostream>

int main() 
{ 
    auto lmd = [](int val){ return val * val; }; 

    std::cout << lmd(2) << ", "; 
    std::cout << lmd(4) << ", "; 
    std::cout << lmd(6); 
}
*****************************
AÇIKLAMA: fonksiyon çağrı operatorünün operandı yapıldı ve parametre olarak capture edilen tür gönderildi.
*****************************
CEVAP: 4, 16, 36
*****************************
int main() 
{ 
    auto lmd = [](int val){ return val + 5; }; 
    cout << typeid(decltype(lmd)).name() << endl; 
}
*****************************
AÇIKLAMA: lambda ifadesinin türü alınarak ismi yazdırılmaktadır.
*****************************
CEVAP: 
*****************************
int main() 
{ 
    [](){ std::cout << "smartcode"; }(); 
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: smartcode
*****************************
int main() 
{ 
   auto lmd = [](){ std::cout << "smartcode"; }; 
   lmd();
}
*****************************
AÇIKLAMA: üstteki kod ile aynı kullanımdır.
*****************************
CEVAP: smartcode
*****************************
auto lmd_func() 
{ 
    return [](int val){ return val * val; }; 
} 

int main() 
{ 
    auto lmd = lmd_func(); 
    std::cout << lmd(10); 
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 100
*****************************
template <typename T> 
void smart_lmd(T lmd) 
{ 
    lmd(); 
} 

int main() 
{ 
    smart_lmd([](){ std::cout << "smartcode"; }); 
}
*****************************
AÇIKLAMA: lambda ifadesi bir fonksiyon şablonundan üretilen fonksiyonun parametresine gönderilir ve deduction ile tür belirlenir.
*****************************
CEVAP: smartcode
*****************************
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>

using namespace std;

int main()  
{  
    std::vector<std::string> svec{ "ali", "can", "1234", "smart" }; 

    std::list<std::string> slist;  

    copy_if(svec.begin(), svec.end(), back_inserter(slist), 
         [](const string& str) { return str.length() == 3; }); 
     
    std::cout << "count : " << slist.size() << std::endl; 

    for (auto str : slist) 
    {
        cout << str << ", "; 
    }
}
*****************************
AÇIKLAMA: first ve last aralığındaki ifadeleri başka yere kopyalayan copy_if predicate olarak lambda ifadesi alıyor ve koşula bağlanıyor.
*****************************
CEVAP: 
count : 2
ali, can,
*****************************
#include <iostream>
#include <map>
#include <functional>

struct LmdParams
{
    uint8_t check;
    uint16_t property;
    std::string data;
};
 
enum class Groups : uint8_t {
    LMD_1 = 1U,
    LMD_2 = 2U,
    LMD_3 = 3U,
};

class SmartCode {
public:
    void set_message(const LmdParams& params) 
    {
        std::cout << "setted Lmd data as a : " << params.data << std::endl;
    }
};

int main() 
{
    std::map<uint8_t, std::function<void(const LmdParams&, SmartCode&)>> handler;

    handler[static_cast<uint8_t>(Groups::LMD_1)] = [](const LmdParams& params, SmartCode& lmd) {
        lmd.set_message(params); };

    LmdParams params;
    params.data = "smartcode";

    SmartCode sc_val;
    uint8_t lmd_type = static_cast<uint8_t>(Groups::LMD_1);

    if (handler.find(lmd_type) != handler.end()) 
    {
        handler[lmd_type](params, sc_val);
    } 
    else 
    {
        std::cout << "Unknown lmd_type: " << static_cast<int>(lmd_type) << std::endl;
    }

    return 0;
}
*****************************
AÇIKLAMA: Kod içerisinde birden fazla konu işlenmiştir. Ancak konumuz lambda olduğu için dikkatimizi ona vermemiz gerekmelidir. Anlaşılmayan noktalar olursa diğer başlıklarımızda beraber inceliyor olacağız. lambda içerisinde capture edilen parametreler sınıf nesnesi ve struct(POD!) içerisindeki değişkenleri kullanabilmemize olanak tanımıştır. 
*****************************
CEVAP: 
count : 2
ali, can,
*****************************
int main()  
{  
    vector<string>svec{ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
    
    cout << "chose length : ";  
    size_t len;  
    cin >> len; 

    cout << count_if(svec.begin(), svec.end(), [len](string str){ return str.length()==len; }) << endl;  
}
*****************************
AÇIKLAMA: count_if ile predicate olarak verilen lambda ifadesindeki istenilen koşulu sağlayan sayı geri döndürülür.
*****************************
CEVAP: 
*****************************
int main()  
{  
    vector <int> ivec{ 21, 26, 85, 69, 62, 47, 45, 95, 14 }; 

    for (auto vals : ivec) 
    {
        cout << vals << " ";
    }

    if (auto iter = find_if(ivec.begin(), ivec.end(), [](int& sc) { return sc % 5 == 0; 
        }); iter != ivec.end())  
    {  
        cout << "found " << *iter << endl;  
        cout << "index : " << distance(ivec.begin(), iter) << endl;  
    }  
    else  
    {  
        cout << "not found" << endl;  
    }  
} 
*****************************
AÇIKLAMA: find_if ile predicate olarak verilen lambda ifadesindeki istenilen koşulu sağlayan(varsa) ilk elemanı ve kaçıncı indekste olduğu bulunur.
*****************************
CEVAP: 
*****************************
int main()  
{  
    vector<string>svec{ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
     
    vector <string> destvec;  

    copy_if(svec.begin(), svec.end(), back_inserter(destvec), [=](const string& str) { return str.find("art") != string::npos; }); 
     
    cout << "destvec size : " << destvec.size() << endl; 

    for (auto vals : destvec) 
    {
        cout << vals << ", "; 
    }
}
*****************************
AÇIKLAMA: içerisinde istenilen string bulunan elemanlar ve kaç tane olduğu bilgisi yine copy_if aldığı lambda predicate ile alınır.
*****************************
CEVAP: 
*****************************
int main() 
{ 
    const int f = [](int a, int b, int c){ return a * b * c; }(5, 4, 3); 
    cout << f; 
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 60
*****************************

Buraya kadar lambda ifadelerinin farklı işlevler ile kullanım senaryolarına örnekler verdik. Lambda ifadesi oluştururken capture edilen yani yakalanan değişkenler farklı şekillerde olabilmektedir. Şimdi bunlara değinerek örneklerle pekiştirelim ve farklılıklarını görelim...
  1. capture by value -> [=]

  2. capture by reference -> [&]

  3. capture with an initializer -> [val = 10]

  4. capture a template arg, all by value or reference -> [args...] or [&args...]

  5. no capture -> []()

  6. capture this -> [this] or [*this]


Lambda ifadelerinde yakalama yöntemleri yukarıda maddeler halinde sıralanmıştır. Bunları da örneklerle pekiştirerek nerelerde ihtiyaç duyuluyor ve farklılıkları nelerdir bakalım...


#include <iostream>
#include <vector>
#include <functional>

using namespace std;

int main() 
{ 
    vector<string>svec{ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
    vector <string> destvec; 

    char c; 
    cout << "select character: " << endl; 
    cin >> c; 

    copy_if(svec.begin(), svec.end(), back_inserter(destvec), [c](const string& str)                    
    { return str.find(c) != string::npos; }); 

    for (auto vals : destvec)
    {
        cout << vals << " ";
    }
}
*****************************
AÇIKLAMA: bu örnekte lambda scope içinde string içindeki karakterin aranması ve npos değilse bulunması istenmektedir. find fonksiyonunun girilen "c" karaktrerinin kullanabilmesi için capture edilmesi ve lambda içinde kullanılabilmesi sağlanmıştır.
*****************************
CEVAP: 
*****************************
int main () 
{
    auto f = [](int val)-> double{ return val + 5.5; };
}
*****************************
AÇIKLAMA: lambda ifadesinin dönüş değeri programcı tarafından verilebilir.
*****************************
CEVAP: 
*****************************
int main() 
{ 
    auto f = [](int val) 
    { 
        if (val % 2) 
        { 
            return 1.; 
        } 
        else 
        { 
            return 2; 
        } 
    }; 

    cout << f(20);
}
*****************************
AÇIKLAMA: bu lambda ifadesi hem int hem de double değer döndürmektedir. C++ dilinde lambda ifadesi tek türde değer döndürebilmektedir. Ancak dönüş değerini "[](int val)-> double" şeklinde yaparsak geçerli olacaktır.
*****************************
CEVAP: inconsistent error (tür uyumsuzluğu)
*****************************
int main() 
{ 
    int x = 5; 
    int y = 6; 

    auto f = [x, y](int val){ return val % (x + y) == 0; }; 
    cout << boolalpha << f(22); 
}
*****************************
AÇIKLAMA: Sadece x ve y değişkenleri lambda ifadesinde kullanılabilecek şekilde capture edilmiştir.
*****************************
CEVAP: true
*****************************
capture clause
int main() 
{ 
    int x = 5; 
    int y = 6; 

    auto f = [=](int val){ return val % (x + y) == 0; }; 
    cout << boolalpha << f(22); 
}
*****************************
AÇIKLAMA: captur copy all.
burada lambda scope içinde kullanılan tüm değişkenler capture clause ile yakalanmıştır. Bu şekilde dışarıda tanımlanan tüm değişkenler lambda ifadesinin kullanımı için capture edilmiştir. 
*****************************
CEVAP: true
*****************************
lambda ve mutable
int main () 
{ 
    int val = 5; 
     
    auto f = [val](){ ++val; };
    f();
    
    cout << val << endl;
}  
*****************************
AÇIKLAMA: lambda ifadesinde değer olarak yakalanan değişkenler default olarak const ele alınır. lambda ifadesi içinde bu const değişken değiştirilmeye çalışıldığı için hata verecektir. Bunun için mutable kullanılabilir ya da reference olarak capture edilirse const olması durumu çözülecektir.
*****************************
CEVAP: syntax error
*****************************
int main () 
{ 
    int val = 5; 
     
    auto f = [val]()mutable{ ++val; };
    f();
    
    cout << val << endl;
}  
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 5
*****************************
int main () 
{ 
    int val = 5; 
     
    auto f = [&val](){ ++val; };
    f();
    
    cout << val << endl;
}  
*****************************
AÇIKLAMA: reference olarak yakalandığı için lambda ifadesi dışında da değişken değerini koruyacak ve değişkenin değeri artacaktır.
*****************************
CEVAP: 6
*****************************
class SmartCode {
public :
    void foo(int)const;
    void func(int);
};

int main() 
{
    SmartCode sc;
    
    auto f = [sc](int val){ sc.foo(val); };           
    auto f1 = [sc](int val){ sc.func(val); };       
}
*****************************
AÇIKLAMA: ilk çağrı fonksiyon const olduğu için geçerlidir. Ancak ikinci çağrıda non-const üye fonksiyon çağrısı olduğu için hata olacaktır.
*****************************
CEVAP: 
*****************************
int main() 
{ 
    int x = 5; 
    int y = 6; 
    int z = 7; 

    auto f = [=, &y](){}; 
}
*****************************
AÇIKLAMA: y değişkeni dışındakiler copy capture ile yakalamıştır.
*****************************
CEVAP: 
*****************************

Generalized lambda (C++14)
int main() 
{
    auto f = [](auto a, auto b) {return a * b; };

    int f1 = f(5, 10);         
    double f2 = f(3.5, 2.0);  

    std::cout << "f1 : " << f1 << std::endl;
    std::cout << "f2 : " << f2 << std::endl;

    return 0;
}
*****************************
AÇIKLAMA: c++14 standartları ile lambda ifadesinde kullanılan parametrelerin türü auto ile deduction yapılabilmektedir.
*****************************
CEVAP: 
*****************************
#include <iostream>

class SmartCode {
public:
    SmartCode(int x) : val(x) {}

    void show_lmd() 
   {
        auto f = [this](){ cout << "Val in SmartCode: " << val << endl; };

        f();
    }

private:
    int val;
};

int main() 
{
    SmartCode sc(23);
    sc.show_lmd(); 

    return 0;
}
*****************************
AÇIKLAMA: lambda içerisinde this kullanılarak sınıfın tüm değişkenleri capture edilebilmektedir.
*****************************
CEVAP: 
*****************************
ree

​the lambda introducer with an optional capture list

template parameter list (optional)

parameter list(optional when no specifiers added)

mutable, constexpr, consteval, noexcept, attributes(optional)

trailing return type (optional)

lambda code body

[ ]

<params>

()

specifiers exception attr

->ret

{ // codes }





 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comments


bottom of page