当前位置: 首页 > 知识库问答 >
问题:

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

闾丘诚
2023-03-14

这个问题与前一个问题相关,在前一个问题中,我们注意到init捕获mutablelambdas与Boost的范围和迭代器transform不兼容,因为一些相当模糊且嵌套很深的typedef故障可能很难通过破解Boost来解决。射程源。

接受的答案建议将lambda存储在std::function对象中。为了避免潜在的virtual函数调用开销,我编写了两个函数对象,可以作为潜在的解决方法。它们在下面的代码中被称为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!   */
}

不使用lamMutableLambda1编译行,并正确打印MutableLambda2行的1,4,9,16的实例。

然而,标准草案提到

5.1.2 Lambda表达式[expr.prim.lambda]

5 [...] 当且仅当lambda表达式的parameter declaration子句后面没有mutable时,此函数调用运算符或运算符模板被声明为const(9.3.1)。[...]

11对于每个初始捕获,在闭包类型中声明由初始捕获标识符命名的非静态数据成员。此成员不是一个位字段,并且不可更改。[...]

这意味着MutableLambda2不是init捕获mutablelambda表达式的一致性手写替代品。

  • 为什么初始化捕获的实现是可变的lambdas(即非const函数调用操作符)?
  • 为什么看似等价的可变的数据成员与const函数调用操作符的替代被禁止?
  • (奖励)为什么Boost范围和迭代器转换依赖于函数对象运算符()const的事实?

共有2个答案

秋飞鸾
2023-03-14

正如注释中指出的,可变lambda需要一个非const函数调用操作符,以便让函数对象的const引用表示纯函数。

原来我申请的罪魁祸首是升压。迭代器underyling的升压。范围实现的提升::adaptor::转换。在Boost中进行了一些挖掘之后。迭代器文档对transform_iterator的要求,原来(粗体强调我的)

类型UnaryFunction必须是可赋值、可复制构造的,表达式f(*i)必须有效,其中f是类型UnaryFunction常量对象,i是类型迭代器的对象,f(*i)的类型必须是

因此,不能使用lambda编写有状态的非纯函数对象,而必须使用const函数调用operator()和表示状态的mutable数据成员来编写。这一点在本相关问题中也有提及

注意:这里有一个开放的bug报告。

空谦
2023-03-14
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)};
}

上面应该允许您使用lambda,将其包装在force\u const\u调用(中,并调用boost算法,而无需自定义可变对象可调用对象(或者更准确地说,上面将lambda转换为自定义可变对象可调用对象)。

 类似资料:
  • 见演示 为什么常量可变lambda不能接受引用参数?

  • 我在读关于不可变类的书,而使类不可变的方法据说是: 1-使类成为最终类以防止继承 我认为第三个条件是不必要的。当我们使一个变量最终并为它提供任何值时,在那之后,即使通过方法也不能给它赋值(因为一旦赋值给它,最终变量就不能改变)。那么为什么我们需要第三个条件没有setter方法呢? 我是不是理解错了?

  • 问题内容: 我有一类具有各种成员变量的类。有一个构造函数,有getter方法,但没有setter方法。实际上,该对象应该是不变的。 现在我注意到了以下几点:当我使用getter方法获得变量列表时,可以添加新值,依此类推- 可以更改。下次调用此变量时,将返回更改的内容。怎么会这样?我没有再设置它,我只是在做它!使用这种行为是不可能的。那么这里有什么区别? 问题答案: 仅仅因为 对 列表 的引用 是不

  • 考虑以下几点: 为什么第一个版本是编译错误,当我已经声明lambda是可变的,并通过值捕获(我认为是它的副本)? 使用clang(x86_64-apple-darwin14.3.0)和Visual C(vc120)进行测试,这是错误消息的来源。

  • 目录 不变性(immutability)的好处有哪些? 为什么 Redux 需要不变性? 为什么 Redux 对浅比较的使用要求不变性? - 浅比较和深比较有何区别? - Redux 是如何使用浅比较的? - combineReducers 是如何进行浅比较的? - React-Redux 是如何使用浅比较拗的? - React-Redux 是如何使用浅比较来决定组件是否需要重新渲染的? - 为什

  • 不可变数据 ClojureScript 中默认采用不可变数据作为底层实现. cljs 当中实现了 Persistent Data Structure, 虽然是不可变数据, 但创建新数据一般会进行结构复用, 也就是说, 比如下面这个例子, b 在内部实现中就可以复用 a 的某些部分 (def a {:a 1 :b 2}) ; #'cljs.user/a (assoc a :c 3) ; {:a 1,