简体   繁体   English

为什么不能初始捕获可变lambda有可变数据成员?

[英]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 它们在下面的代码中称为MutableLambda1MutableLambda2

#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 . 活实施例 ,将不编译与线lamMutableLambda1 ,和正确打印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表达式的手写替代品。

Questions 问题

  • why is the implementation of init-capturing mutable lambdas the way it is (ie non-const function call operator)? 为什么init-capture mutable lambdas的实现方式(即非const函数调用运算符)?
  • why is the seemingly equivalent alternative of mutable data members with a const function call operator forbidden? 为什么具有const函数调用运算符的mutable数据成员看似等价的替代?
  • ( bonus ) why does the Boost range and iterator 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 expression f(*i) must be valid where f is a const object of type UnaryFunction , i is an object of type Iterator, and where the type of f(*i) must be result_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM