7 Ekim 2020 Çarşamba

Closure - Capture By Reference

1. İsim Vermeden Capture
Closure içinde kullanılan değişkenler referans şeklinde saklanır. Değişkenin ömrünün closure'dan fazla olmasına dikkat etmek gerekir. Açıklaması şöyle
a comma-separated list of zero or more captures, optionally beginning with a capture-default. Capture list can be passed as follows [...]:

[a,&b] where a is captured by value and b is captured by reference.

[this] captures the this pointer by value

[&] captures all automatic variables odr-used in the body of the lambda by reference

[=] captures all automatic variables odr-used in the body of the lambda by value

[] captures nothing
Üretilen Kod
Elimize şöyle bir closure olsun.
[&] { x += 1; }       // capture by reference
Derleyici şöyle bir kod üretir.
class foo { 
    int &x;
public:
    foo(int &x) : x(x) {}
    void operator()() const { x += 1; }
};
Kullanım
İsim vermeden capture ve isim vererek capture arasındaki fark şöyle
int main()
{
  int num = 50;
  [&] { std::cout << num << '\n'; }(); // num captured by reference
  [=] { std::cout << num << '\n'; }(); // num captured by value

  [&num] { std::cout << num << '\n'; }(); // by reference
  [num] { std::cout << num << '\n'; }();  // by value
}
Örnek - dangling reference
Elimizde şöyle bir kod olsun
#include <iostream>
#include <functional>

std::function<void ()> f()
{
  int x = 666;
  return [&] { std::cout << x << std::endl; };
}

int main()
{
  f()();
  return 0;
}
Bu kod hatalı. Açıklaması şöyle.
You capture x by reference in the lambda and after leaving f() it becomes a dangling reference as x gets destroyed. You have a classic UB. To avoid it you can capture x by value by writing [x] or [=] instead of [&]
Örnek
Elimizde şöyle bir kod olsun. Burada [&] ile hem yerel değişkenler hem de this kullanılabiliyor. 
Rect rect;
Point point;

auto someLambda = [&](const SomeType& var)
{
  if (rect.Contains(point))
  {
    var.Something();
  }

  this->MemberFunction();
};
Açıklaması şöyle
This ends up grabbing rect and point by reference and also gives you access to this...
2. İsim İle Capture
Örnek
Şöyle yaparız. Sadece cap değişkenine erişileceği belirtilir.
int main() { 
  const int ci = 0;
  auto &cap = ci;
  auto lambda = [&cap]() { };
}
Örnek
İsim ile capture ve isim olmadan capture arasındaki fark şöyle.
vector<int> vec;

//a
auto foo = [&vec](){
  //do something
};

//b
auto foo = [&v = vec](){
  //do something
};
2.1 İsim İle Capture ve const Değişken
Yakalama işleminde değişkenin const olup olmadığı önemlidir. const bir değişken değiştirilemez.
const int i = 5;
auto b = [&i]() { i++; }; //error on i++
2.2 İsim İle Capture ve const Closure
Şöyle yapılır. Lambda kendi içindeki state bilgisini değiştiremez.
int foo(int i)
{
  const auto a = [&i](){...};
  ...
}
2.2 İsim İle Capture ve Değişkene Farklı İsim Vermek
Örnekte ci değişkenine lamda içinde cap değişkeni olarak erişilir. Yani sadece değişkene farklı bir isim tanımlanır.
int main() { 
  const int ci = 0;
  auto lambda = [ &cap = ci ]() { };
}
Cast ile Capture
Saçma gelse bile capture ederken cast işlemi yapılabiliyor. Const olmayan bir değişken const yapılabiliyor.
#include <iostream>
#include <type_traits>

struct noncopyable
{
    int a;
    const noncopyable& operator=(noncopyable&&) = delete;
    noncopyable(noncopyable&&) = delete;
    const noncopyable& operator=(const noncopyable&) = delete;
    noncopyable(const noncopyable&) = delete;
    ~noncopyable() = default;
};

void modify(noncopyable& a)
{
    a.a = 0;
}

int main()
{
    noncopyable a = { 25 }, b = { 42 };
    [&, &a = static_cast<const noncopyable&>(a)]{
        modify(b);
        //modify(a); // uncommenting this should fail the build
    }();
    std::cout << a.a << " " << b.a << "\n";
    // expected output 25 0
}

Hiç yorum yok:

Yorum Gönder