Bir lambda lambda kadar süren bir iç değer nasıl verilir?

Çevreleyen alanı etkilemeden bir lambda içinde değiştirebileceğim bir değişkene sahip olmak istiyorum. Böyle davranan bir şey:

std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
{
  auto sum = 0;
  std::for_each(vec.begin(), vec.end(), [sum](int value) mutable
  {
    sum += value;
    std::cout << "Sum is up to: " << sum << '/n';
  });
}

Ancak, lambda dışında sum değişkenini bildirmeden bunu yapabilmek istiyorum. Bunun gibi bir şey:

std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

std::for_each(vec.begin(), vec.end(), [auto sum = 0](int value) mutable
{
  sum += value;
  std::cout << "Sum is up to: " << sum << '/n';
});

Öyleyse sum , yalnızca kapsam dahilinde değil, lambda içinde görünür. C ++ 11/14'te mümkün mü?

3
auto 'yı kaldırın ve 11 ' ı 14 ile değiştirin;
katma yazar Kerrek SB, kaynak
@KerrekSB Bu harika! Teşekkürler :)
katma yazar Kian, kaynak
@KerrekSB Bu harika! Teşekkürler :)
katma yazar Kian, kaynak

7 cevap

C ++ 14, istediğiniz şeyi yapmanıza izin veren Genelleştirilmiş Lambda Yakalama özelliğini sunar.

Yakalama, init ifadesinin türünden auto ile düşülür.

[sum = 0] (int value) mutable {
   //'sum' has been deduced to 'int' and initialized to '0' here.
    /* ... */
}
9
katma
İlginç, ilk defa birisinin bu özelliğe bu isimden bahsettiğini görüyorum. Genellikle init-capture veya "generalized lambda capture" görürüm.
katma yazar T.C., kaynak
@ T.Ç. Haklısın, genellikle bu isimlerle anılır. Düzenlenen.
katma yazar Snps, kaynak

C ++ 14, istediğiniz şeyi yapmanıza izin veren Genelleştirilmiş Lambda Yakalama özelliğini sunar.

Yakalama, init ifadesinin türünden auto ile düşülür.

[sum = 0] (int value) mutable {
   //'sum' has been deduced to 'int' and initialized to '0' here.
    /* ... */
}
9
katma
İlginç, ilk defa birisinin bu özelliğe bu isimden bahsettiğini görüyorum. Genellikle init-capture veya "generalized lambda capture" görürüm.
katma yazar T.C., kaynak
@ T.Ç. Haklısın, genellikle bu isimlerle anılır. Düzenlenen.
katma yazar Snps, kaynak

Eğer C ++ 11'e takıldıysanız (ve C ++ 14'ün lambda yakalama ifadelerini kullanamıyorsanız, bkz. @Snps'nin cevabı), lambda içinde başka bir fonksiyonda yaptığınız gibi statik bir değişken tanımlayabilirsiniz. Aşağıdaki örnek:

#include 
#include 
#include 

int main()
{
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    std::for_each(vec.begin(), vec.end(), [](int value)
    {
        static decltype(vec)::value_type sum{};
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    });
}
0
katma
@greggo, tam olarak neyi başarmaya çalıştığına bağlı. Aynı lambda'yı (yani, kapaktan üretilen nesne) "yeniden" kullanabildiğiniz için, yakalama ifadeleri kullanmanın bu açıdan daha iyi olduğunu kabul ediyorum.
katma yazar vsoftco, kaynak
@greggo "Ne demek istediğimi anladığımdan emin değilim" Bana "toplam" ın başlatılmasının yalnızca çalıştırma başına bir kez gerçekleşeceğini gösteriyor. " sum yalnızca ilk olarak başlatılır. Herhangi bir statik var gibi arama. düzenli bir işlevde. Bağlantı için teşekkürler.
katma yazar vsoftco, kaynak
.. Eğer bunu "main" yerine "sumvec" adlı bir fonksiyona koyarsanız, "sumvec" için ikinci çağrı çalışmayacaktır, çünkü "sum" yalnızca bir kez başlatılır.
katma yazar greggo, kaynak
Bence bunun ötesine geçiyor. Her "sumarr" çağrısı, her iki durumda da yeni bir lambda nesnesi oluşturur, ancak statik değişken, tüm durumlarda ortaktır. çağrıdan çağrıya değişen statik yerlilerin çok sınırlı kullanımları var, bunlardan biri demezdim.
katma yazar greggo, kaynak
Bana göre "sum" un başlatılmasının sadece çalıştırma başına bir kez gerçekleşeceği görülüyor. Bu örnekte "asıl" önemi yoktur, ancak genel durumda bu bir problemdir. Ayrıca, statik staentence (ve optimizasyon kısıtlayabilir) kırmak. Bkz. stackoverflow.com/questions/8391058/…
katma yazar greggo, kaynak

Eğer C ++ 11'e takıldıysanız (ve C ++ 14'ün lambda yakalama ifadelerini kullanamıyorsanız, bkz. @Snps'nin cevabı), lambda içinde başka bir fonksiyonda yaptığınız gibi statik bir değişken tanımlayabilirsiniz. Aşağıdaki örnek:

#include 
#include 
#include 

int main()
{
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    std::for_each(vec.begin(), vec.end(), [](int value)
    {
        static decltype(vec)::value_type sum{};
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    });
}
0
katma
@greggo, tam olarak neyi başarmaya çalıştığına bağlı. Aynı lambda'yı (yani, kapaktan üretilen nesne) "yeniden" kullanabildiğiniz için, yakalama ifadeleri kullanmanın bu açıdan daha iyi olduğunu kabul ediyorum.
katma yazar vsoftco, kaynak
@greggo "Ne demek istediğimi anladığımdan emin değilim" Bana "toplam" ın başlatılmasının yalnızca çalıştırma başına bir kez gerçekleşeceğini gösteriyor. " sum yalnızca ilk olarak başlatılır. Herhangi bir statik var gibi arama. düzenli bir işlevde. Bağlantı için teşekkürler.
katma yazar vsoftco, kaynak
.. Eğer bunu "main" yerine "sumvec" adlı bir fonksiyona koyarsanız, "sumvec" için ikinci çağrı çalışmayacaktır, çünkü "sum" yalnızca bir kez başlatılır.
katma yazar greggo, kaynak
Bana göre "sum" un başlatılmasının sadece çalıştırma başına bir kez gerçekleşeceği görülüyor. Bu örnekte "asıl" önemi yoktur, ancak genel durumda bu bir problemdir. Ayrıca, statik staentence (ve optimizasyon kısıtlayabilir) kırmak. Bkz. stackoverflow.com/questions/8391058/…
katma yazar greggo, kaynak
Bence bunun ötesine geçiyor. Her "sumarr" çağrısı, her iki durumda da yeni bir lambda nesnesi oluşturur, ancak statik değişken, tüm durumlarda ortaktır. çağrıdan çağrıya değişen statik yerlilerin çok sınırlı kullanımları var, bunlardan biri demezdim.
katma yazar greggo, kaynak

En azından bence, bunu yapmanın daha iyi yolları var. std :: for_each öğesini kullanıyorsunuz, ancak bunu std :: biriktirmek 'i taklit etmek için kullanıyorsunuz. İkincisi eldeki iş için net bir seçimdir:

#include 
#include 
#include 

int main(){
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    std::accumulate(vec.begin(), vec.end(), 0, [](int sum, int val) {
        sum += val;
        std::cout << "sum is up to: " << sum << "\n";
        return sum; });
}

Gerçekten senin için önemli olup olmadığından emin değilim, ama bu C ++ 11'in ötesinde bir özellik gerektirmiyor.

0
katma
"Önceki çağrıdan bir değeri koruyan ve üzerinde bir işlem uygulayan bir değişkenin" bir tür toplayıcı olduğunu görebiliyorum. Örneğin, bir dizin istiyorsam, her yinelemede bir tane toplar ve eklerdim. Ancak, std :: biriktirici akümülatörü döndürür. Lambda içinden başka bir yapıya indekslemeniz gerektiğinden bir indeks kullanmak istiyorsanız, isim yanıltıcıdır. Biriktirme bir uygulama detayıdır, hiçbir şey biriktirmek umrumda değil. 6 ay sonra kodu okuyan biri neden aradığınızı merak ediyor olabilir ve geri dönüş değerini atar.
katma yazar Kian, kaynak
Örnek sadece açıklama amaçlıdır. Biriktirmek gerçekten uygun olmaz ve isim incelemede kafa karıştırıcı olurdu. Yine de algoritmalarda başka fonksiyonlar olduğunu hatırlamakta fayda var. Bir şeyler yapmadan önce her zaman kontrol etmiyorum.
katma yazar Kian, kaynak
@Kian: Yine de istediğin şey, lambda'nın bir çağrısından diğerine kadar bir şey (bir şey) biriktiren bir değerdi ...
katma yazar Jerry Coffin, kaynak

En azından bence, bunu yapmanın daha iyi yolları var. std :: for_each öğesini kullanıyorsunuz, ancak bunu std :: biriktirmek 'i taklit etmek için kullanıyorsunuz. İkincisi eldeki iş için net bir seçimdir:

#include 
#include 
#include 

int main(){
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    std::accumulate(vec.begin(), vec.end(), 0, [](int sum, int val) {
        sum += val;
        std::cout << "sum is up to: " << sum << "\n";
        return sum; });
}

Gerçekten senin için önemli olup olmadığından emin değilim, ama bu C ++ 11'in ötesinde bir özellik gerektirmiyor.

0
katma
"Önceki çağrıdan bir değeri koruyan ve üzerinde bir işlem uygulayan bir değişkenin" bir tür toplayıcı olduğunu görebiliyorum. Örneğin, bir dizin istiyorsam, her yinelemede bir tane toplar ve eklerdim. Ancak, std :: biriktirici akümülatörü döndürür. Lambda içinden başka bir yapıya indekslemeniz gerektiğinden bir indeks kullanmak istiyorsanız, isim yanıltıcıdır. Biriktirme bir uygulama detayıdır, hiçbir şey biriktirmek umrumda değil. 6 ay sonra kodu okuyan biri neden aradığınızı merak ediyor olabilir ve geri dönüş değerini atar.
katma yazar Kian, kaynak
Örnek sadece açıklama amaçlıdır. Biriktirmek gerçekten uygun olmaz ve isim incelemede kafa karıştırıcı olurdu. Yine de algoritmalarda başka fonksiyonlar olduğunu hatırlamakta fayda var. Bir şeyler yapmadan önce her zaman kontrol etmiyorum.
katma yazar Kian, kaynak
@Kian: Yine de istediğin şey, lambda'nın bir çağrısından diğerine kadar bir şey (bir şey) biriktiren bir değerdi ...
katma yazar Jerry Coffin, kaynak

C ++ 14 mevcut değilse, her şeyi orijinal lambdaya döndüren bir lambdaya sarabilir ve derhal arayabilirsin.

std::for_each(vec.begin(), vec.end(), []()
{
    auto sum = 0;
    return [sum](int value) mutable
    {
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    };
}());

Bu şekilde geçici sum 'ın ömrü minimumdur.

0
katma