C ++ Lambdas, Capturing, Smart Ptrs и стек: почему это работает? - PullRequest
8 голосов
/ 19 декабря 2011

Я поиграл с некоторыми новыми функциями в C ++ 11 и попытался написать следующую программу, ожидая, что она не будет работать.К моему большому удивлению, это происходит (в GCC 4.6.1 в Linux x86 с флагом 'std = c ++ 0x'):

#include <functional>
#include <iostream>
#include <memory>

std::function<int()> count_up_in_2s(const int from) {
    std::shared_ptr<int> from_ref(new int(from));
    return [from_ref]() { return *from_ref += 2; };
}

int main() {
    auto iter_1 = count_up_in_2s(5);
    auto iter_2 = count_up_in_2s(10);

    for (size_t i = 1; i <= 10; i++)
        std::cout << iter_1() << '\t' << iter_2() << '\n'
        ;
}

Я ожидал, что «from_ref» будет удален при каждом выполнениииз возвращенных лямбда-трасс.Вот мое рассуждение: после запуска count_up_in_2s, from_ref выталкивается из стека, но поскольку возвращаемая лямбда не обязательно запускается сразу, так как она возвращается, в течение короткого периода не существует другой ссылки, пока эта же ссылка не будетотодвинулся, когда лямбда фактически запускается, поэтому не должен ли счетчик ссылок shared_ptr достигать нуля, а затем удалять данные?

Если захват лямбда-кода в C ++ 11 не намного умнее, чем я его даюкредит на который, если это так, я буду рад.Если это так, могу ли я предположить, что захват переменных в C ++ 11 разрешит все лексические хитрости определения / закрытия а-ля Лисп, пока / что-то / заботится о динамически распределенной памяти?Могу ли я предположить, что все захваченные ссылки будут оставаться в живых до тех пор, пока сама лямбда не будет удалена, что позволит мне использовать smart_ptrs вышеуказанным способом?

Если это так, как я думаю, это не означает, что C ++11 позволяет выразительное программирование высшего порядка?Если так, то я думаю, что комитет C ++ 11 проделал отличную работу =)

Ответы [ 3 ]

7 голосов
/ 19 декабря 2011

Лямбда захватывает from_ref по значению, поэтому делает копию.Из-за этой копии количество ссылок не равно 0, когда from_ref уничтожается, оно равно 1 из-за копии, которая все еще существует в лямбде.

4 голосов
/ 19 декабря 2011

Следующее:

std::shared_ptr<int> from_ref(new int(from));
return [from_ref]() { return *from_ref += 2; };

в основном эквивалентно этому:

std::shared_ptr<int> from_ref(new int(from));
class __uniqueLambdaType1432 {
  std::shared_ptr<int> capture1;
 public:        
  __uniqueLambdaType1432(std::shared_ptr<int> capture1) :
    capture1(capture1) { 
  }
  decltype(*capture1 += 2) operator ()() const {
    return *capture1 += 2;
  }
};
return __uniqueLambdaType1432(from_ref);

, где __uniqueLambdaType1432 - это программно-глобально уникальный тип, отличный от любых других, даже других лямбда-типовгенерируется лексически идентичным лямбда-выражением.Его действительное имя недоступно для программиста, и, кроме объекта, полученного из исходного лямбда-выражения, никакие другие его экземпляры не могут быть созданы, потому что конструктор фактически скрыт магией компилятора.

1 голос
/ 15 октября 2012

from_ref захватывается значением.

Ваше рассуждение работает, если вы замените

return [from_ref]() { return *from_ref += 2; };

с

return [&from_ref]() { return *from_ref += 2; };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...