Этот вопрос связан с этот предыдущий где было замечено, что инициализация захвата mutable
лямбды несовместимы с диапазоном и итератором 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
,
Тем не менее проект стандарта упоминает
5.1.2 Лямбда-выражения [expr.prim.lambda]
5 […] Этот оператор вызова функции или шаблон оператора объявлен как const
(9.3.1) тогда и только тогда, когда лямбда-выражение
Параметр-объявление-предложение не сопровождаетсяmutable
, […]11 Для каждого init-захвата нестатический элемент данных, названный
Идентификатор init-capture объявлен в типе замыкания. это
участник не поле биты и неmutable
, […]
Это означает, что MutableLambda2
не является соответствующей заменой рукописной записи для инициализации mutable
лямбда-выражение.
mutable
лямбда, как он есть (т.е. оператор вызова неконстантной функции)?mutable
члены данных с const
оператор вызова функции запрещен? transform
полагаться на тот факт, что функция объектов operator()
является const
?template<class L>
struct force_const_call_t {
mutable L f;
template<class...Args>
auto operator()(Args&&...args) const
{ return f(std::forward<Args>(args)...); }
};
template<class L>
force_const_call_t<L> force_const_call(L&&f){
return {std::forward<L>(f)};
}
вышеупомянутое должно позволить вам взять лямбду, обернуть это в force_const_call(
… )
и позвоните boost
алгоритм, без кастома mutable
вызываемый объект (или, точнее, вышеупомянутый превращает лямбды в обычай mutable
вызываемые объекты).
Как отмечено в комментариях, изменяемая лямбда требует неконстантного оператора вызова функции, чтобы позволить постоянным ссылкам на функциональные объекты представлять чистые функции.
Оказывается, виновником моего приложения является Boost.Iterator, лежащий в основе реализации Boost.Range boost::adaptors::transformed
, После некоторых копаний в документации Boost.Iterator требования к transform_iterator
, получается что (жирный акцент мой)
Тип
UnaryFunction
должен быть назначаемым, копируемым, и
выражениеf(*i)
должен быть действительным гдеf
является постоянным объектом типа
UnaryFunction
,i
является объектом типа Iterator, а где тип
f(*i)
должно бытьresult_of<const
,
UnaryFunction(iterator_traits<Iterator>::reference)>::type
Поэтому объекты с непостоянной функцией не могут быть написаны с использованием лямбда-выражений, а должны быть написаны с использованием const
вызов функции operator()
и с mutable
члены данных, представляющие государство. Это было также отмечено в это связано Q&.
Заметка: есть открыть отчет об ошибке за это.