Допустим, у меня есть два локальных умных указателя, foo
а также bar
,
shared_ptr<Foo> foo = ...
shared_ptr<Bar> bar = ...
Эти умные указатели являются обертками вокруг ресурсов, которые по какой-то причине должны быть уничтожены в порядке foo
, затем bar
,
Теперь я хочу создать лямбду, которая использует foo
а также bar
, но переживает объем, содержащий их. Поэтому я бы взял их по значению, например так:
auto lambda = [foo, bar]() { ... };
Это создает копии foo
а также bar
внутри объекта функции. Когда функциональный объект уничтожен, эти копии также будут уничтожены, но я забочусь о порядке, в котором это происходит. Итак, мой вопрос:
Когда разрушается лямбда-объект, в каком порядке уничтожаются его значения по значению? И как я могу (надеюсь) повлиять на этот порядок?
Спекуляция покрывает это … вроде. С 5.1.2, пункт 14:
Сущность захватывается копией, если она неявно захвачена, а значение по умолчанию для захвата равно = или если она явно захвачена с помощью захвата, который не включает &, Для каждого объекта, захваченного копией, в типе замыкания объявляется неназванный элемент не статических данных. Порядок объявления этих членов не уточняется.
Акцент добавлен. Поскольку порядок объявления не указан, порядок построения не определен (поскольку порядок построения совпадает с порядком объявления). И, следовательно, разрушение порядок не указан, так как порядок уничтожения является обратным порядку построения.
Короче говоря, если вам нужно позаботиться о порядке декларирования (и о различных заказах на строительство / уничтожение, которые его определяют), вы не могу использовать лямбду. Вам нужно будет сделать свой собственный тип.
Вместо того, чтобы беспокоиться о порядке уничтожения, вы должны исправить тот факт, что это проблема. Отметив, что вы используете общие указатели для обоих объектов, вы можете обеспечить порядок уничтожения, добавив общий указатель в объект, который вам нужен, чтобы пережить другой. В этот момент ли foo
или же bar
уничтожен раньше не будет иметь значения. Если порядок правильный, уничтожение общего указателя немедленно освободит объекты. Если порядок неправильный, дополнительный разделяемый указатель будет поддерживать объект живым, пока другой не исчезнет.
Как говорит Николь, порядок уничтожения не определен.
Однако вам не нужно зависеть от уничтожения лямбды. Вы должны быть в состоянии просто сбросить foo
в конце вашей лямбды, таким образом гарантируя, что он освобождает свой ресурс до bar
делает. Вам также придется пометить лямбду как mutable
хоть. Единственным недостатком здесь является то, что вы не можете вызывать лямбда несколько раз и ожидать, что он сработает.
auto lambda = [foo, bar]() mutable { ...; foo.reset(); };
Если вам нужно, чтобы ваша лямбда могла вызываться несколько раз, вам нужно найти другой способ управления порядком освобождения. Одним из вариантов будет использование промежуточной структуры с известным порядком элементов данных, такой как std::pair<>
:
auto p = std::make_pair(bar, foo);
auto lambda = [p]() { auto foo = p.second, bar = p.first; ... };
В соответствии с документом C ++ 11, который у меня есть (т.е. халява, немного предшествующая ратификации n3242), раздел 5.1.2, пункт 21, перехваты создаются в порядке декларации и уничтожаются в порядке обратной декларации. Однако порядок декларирования не уточняется (пункт 14). Таким образом, ответ таков: «в неопределенном порядке» и «вы не можете на это влиять» (за исключением, я полагаю, написания компилятора).
Если bar действительно необходимо уничтожить перед foo, было бы целесообразно, чтобы bar содержал общий указатель на foo (или что-то в этом роде).