[英]Why can't init-capturing mutable lambdas have mutable data members?
This question is related to this previous one where it was noticed that init-capturing mutable
lambdas are incompatible with Boost's range and iterator transform
for some rather obscure and deeply nested typedef
failures that may or may not be easy to resolve through hacking the Boost.Range sources. 这个问题关系到这个前一个它被发现的init捕获mutable
lambda表达式与Boost的范围和迭代器不兼容的transform
对于一些比较模糊和深度嵌套typedef
故障,可能会或可能不容易通过黑客Boost.Range解决源。
The accepted answer suggested storing the lambda in a std::function
object. 接受的答案建议将lambda存储在std::function
对象中。 To avoid potential virtual
function call overhead, I wrote two function objects that could serve as potential work-arounds. 为了避免潜在的virtual
函数调用开销,我写了两个函数对象,可以作为潜在的解决方法。 They are called MutableLambda1
and MutableLambda2
in the code below 它们在下面的代码中称为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! */
}
Live Example that won't compile the lines with lam
and MutableLambda1
, and correctly prints 1, 4, 9, 16
for the line with MutableLambda2
. 活实施例 ,将不编译与线lam
和MutableLambda1
,和正确打印1, 4, 9, 16
,用于与线路MutableLambda2
。
However, the draft Standard mentions 但是, 标准草案提到了
5.1.2 Lambda expressions [expr.prim.lambda] 5.1.2 Lambda表达式[expr.prim.lambda]
5 [...] This function call operator or operator template is declared const (9.3.1) if and only if the lambda-expression's parameter-declaration-clause is not followed by
mutable
. 5 [...]当且仅当lambda-expression的parameter-declaration-clause后面没有mutable
此函数调用操作符或操作符模板才被声明为const(9.3.1)。 [...] [...]11 For every init-capture a non-static data member named by the identifier of the init-capture is declared in the closure type. 11对于每个init-capture,在闭包类型中声明由init-capture的标识符命名的非静态数据成员。 This member is not a bit-field and not
mutable
. 这个成员不是一个现场,也不是mutable
。 [...] [...]
This means that MutableLambda2
is not a conforming handwritten replacement for an init-capturing mutable
lambda expression. 这意味着MutableLambda2
不是一个符合init的捕获mutable
lambda表达式的手写替代品。
mutable
lambdas the way it is (ie non-const function call operator)? 为什么init-capture mutable
lambdas的实现方式(即非const函数调用运算符)? mutable
data members with a const
function call operator forbidden? 为什么具有const
函数调用运算符的mutable
数据成员看似等价的替代? transform
rely on the fact that a function objects operator()
is const
? ( 奖励 )为什么Boost范围和迭代器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)};
}
the above should let you take a lambda, wrap it in force_const_call(
... )
, and call your boost
algorithm, without a custom mutable
callable object (or more precisely, the above turns lambdas into custom mutable
callables). 上面的内容应该让你获取一个lambda,将它包装在force_const_call(
... )
,然后调用你的boost
算法,而不需要一个自定义的mutable
调用对象(或者更准确地说,上面将lambdas变成自定义的mutable
调用对象)。
As pointed out in the comments, a mutable lambda requires a non-const function call operator in order to let const references to function objects represent pure functions. 正如注释中所指出的,一个可变的lambda需要一个非const函数调用操作符,以便让对函数对象的const引用表示纯函数。
It turns out that the culprit for my application is Boost.Iterator underyling the Boost.Range implementation of boost::adaptors::transformed
. 事实证明,我的应用程序的罪魁祸首是Boost.Iterator,它基于boost::adaptors::transformed
的Boost.Range实现。 After some digging in the Boost.Iterator documentation's requirements for transform_iterator
, it turns out that (bold emphasis mine) 在深入了解Boost.Iterator文档对transform_iterator
的要求之后 ,事实证明(大胆强调我的)
The type
UnaryFunction
must be Assignable, Copy Constructible, and the expressionf(*i)
must be valid wheref
is a const object of typeUnaryFunction
,i
is an object of type Iterator, and where the type off(*i)
must beresult_of<const UnaryFunction(iterator_traits<Iterator>::reference)>::type
. 类型UnaryFunction
必须是Assignable,Copy Constructible,并且表达式f(*i)
必须有效,其中f
是UnaryFunction
类型的const对象 ,i
是Iterator类型的对象,并且f(*i)
的类型必须是是result_of<const UnaryFunction(iterator_traits<Iterator>::reference)>::type
。
Stateful non-pure function objects can therefore not be written using lambdas but instead have to written using a const
function call operator()
and with mutable
data members representing the state. 因此,有状态的非纯函数对象不能使用lambdas编写,而是必须使用const
函数调用operator()
和表示状态的mutable
数据成员编写。 This was also remarked in this related Q&A . 在此相关问答中也提到了这一点 。
Note : there is an open bug report for this. 注意 :有一个开放的错误报告 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.