Этот вопрос связан с предыдущим, где было замечено, что mutable
лямбды init-capture несовместимы с диапазоном Boost и итератором transform
для некоторых довольно неясных и глубоко вложенных сбоев typedef
, которые могут или не могут быть легко устранены путем взлома исходников Boost.Range.
В принятом ответе предлагалось хранить лямбда в объекте std::function
. Чтобы избежать потенциальных накладных расходов на вызов функции virtual
, я написал два объекта функций, которые могли бы служить потенциальными обходными путями. В приведенном ниже коде они называются MutableLambda1
и MutableLambda2
.
#include <iostream>
#include <iterator>
#include <vector>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
// this version is conforming to the Standard
// but is not compatible with boost::transformed
struct MutableLambda1
{
int delta;
template<class T> auto operator()(T elem) { return elem * delta++; }
};
// Instead, this version works with boost::transformed
// but is not conforming to the Standard
struct MutableLambda2
{
mutable int delta;
template<class T> auto operator()(T elem) const { return elem * delta++; }
};
// simple example of an algorithm that takes a range and laziy transformes that
// using a function object that stores and modifies internal state
template<class R, class F>
auto scale(R r, F f)
{
return r | boost::adaptors::transformed(f);
}
int main()
{
// real capturing mutable lambda, will not work with boost::transformed
auto lam = [delta = 1](auto elem) mutable { return elem * delta++; };
auto rng = std::vector<int>{ 1, 2, 3, 4 };
//boost::copy(scale(rng, lam), std::ostream_iterator<int>(std::cout, ",")); /* ERROR */
//boost::copy(scale(rng, MutableLambda1{1}), std::ostream_iterator<int>(std::cout, ",")); /* ERROR */
boost::copy(scale(rng, MutableLambda2{1}), std::ostream_iterator<int>(std::cout, ",")); /* OK! */
}
Живой пример, который не компилирует строки с lam
и MutableLambda1
, и правильно печатает 1, 4, 9, 16
для строки с MutableLambda2
.
Однако проект стандарта strong> упоминает
5.1.2 Лямбда-выражения [expr.prim.lambda]
5 [...] Этот оператор вызова функции или шаблон оператора объявляется const (9.3.1) тогда и только тогда, когда за пунктом объявления параметра лямбда-выражения не следует
mutable
. [...]11 Для каждого init-capture нестатический элемент данных, названный идентификатором init-capture, объявляется в типе замыкания. Этот элемент не является битовым полем и не
mutable
. [...]
Это означает, что MutableLambda2
не является подходящей рукописной заменой для лямбда-выражения mutable
, захватывающего инициализацию.
Вопросы
- почему реализация init-capture
mutable
lambdas такая, какая она есть (т.е. неконстантный оператор вызова функции)? - почему кажущаяся эквивалентной альтернатива
mutable
элементов данных сconst
оператором вызова функции запрещена? - (бонус), почему диапазон Boost и итератор
transform
основаны на том факте, что объект функцииoperator()
равенconst
?
MutableLambda2
работает, но не имеет такой же реализации, как Стандарт предписывает для лямбда-выражений. Интересно, почему это так, и есть ли у моего обходного пути какие-либо скрытые ловушки. - person TemplateRex   schedule 23.02.2014MutableLambda2
не соответствует Стандарту. В каком смысле? Как реализация лямбда-выражений? -- Изменяемые лямбда-выражения с неконстантными операторами вызова функций требуют вызова неконстантных объектов; Константные операторы вызова функций OTOH +mutable
члены данных этого не делают. Это та разница, которую вы ищете? - person dyp   schedule 23.02.2014MutableLambda2
вместоMutableLambda1
(как сейчас требуется), как он сломает пользовательский код? Как вы указываете, вы могли бы вызывать первое для объектов const, но почему это плохо? (поскольку он изменяет только внутреннее состояние, а не аргументы, передаваемые вoperator()
). - person TemplateRex   schedule 23.02.2014a
иb
. Когда оператор вызова функции является константой, вы можете разумно предположить, что использование функцииa
и использование функцииb
взаимозаменяемы. - person dyp   schedule 23.02.2014MutableLambda2
в качестве хорошо прокомментированного обходного пути для моей проблемы, пока Boost не догонит лямбда-выражения, захватывающие инициализацию. - person TemplateRex   schedule 23.02.2014