在C ++中模拟Lambda的复制分配运算符 [英] Emulate copy-assignment operator for lambdas in C++

查看:95
本文介绍了在C ++中模拟Lambda的复制分配运算符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题分为两个部分

首先,有人可以解释一下C ++禁用lambda的复制分配运算符的原理吗?如果要允许使用复制构造函数,为什么不使用复制分配运算符呢?

Firstly, can someone explain the rationale behind C++ disabling the copy-assignment operator for lambdas? If you're going to allow the copy constructor, why not the copy-assignment operator?

第二,如何最好地克服这一限制不会强迫人们编写C ++ 03样式函子,也不会使用std :: function(我要处理的函数很小,我希望编译器尽可能内联它们)?

Secondly, how do you best overcome this limitation without forcing people to write C++03 style functors, or using std::function (the functions I'm dealing with are tiny, and I'd like the compiler to inline them wherever possible)?

背景
我正在尝试在我正在编写的流处理库,类似于Scala或其他功能语言中的flatMap。结果,我需要创建一个迭代器,该迭代器遍历迭代器列表。每次对flat_map迭代器取消引用时,都会执行与内部迭代器关联的lambda。每当内部迭代器到达末尾时,外部迭代器就需要切换内部迭代器。由于内部迭代器包含lambda,因此没有复制分配运算符,因此无法进行切换。从技术上讲,我可以使用动态分配解决问题,所以我总是称呼复制构造函数,但这似乎不是正确的方法。以下是一小段代码,可以帮助您指出问题所在:

Background: I'm trying to implement a flat_map like operation in a stream processing library I'm writing, similar to flatMap in Scala or other functional languages. As a result, I need to create an iterator that iterates over a list of iterators. Each time the flat_map iterator is de-referenced a lambda associated with the inner iterator is executed. The outer iterator needs to switch the inner iterator each time the inner iterator reaches the end. Since the inner iterator contains a lambda, and therefore does not have a copy-assignment operator, it's not possible to switch it. Technically I could solve the problem using dynamic allocation, so that I always call the copy-constructor, but that doesn't seem like the right approach. Here is a snippet of code that might help highlight the problem:

template <typename Iter>
class flat_map_iterator {
public:
  flat_map_iterator& operator++() {
    ++it_inner_;
    if (it_inner_ == (*it_outer_).end()) {
      ++it_outer_;
      // ERROR: cannot be assigned because its copy assignment operator is implicitly deleted
      it_inner_ = (*it_outer_).begin();
    }
    return *this;
  }
private:
  Iter it_outer_; 
  typename Iter::value_type::iterator it_inner_;
};

编辑

感谢您的快速回复。这是一个用例示例:

Thanks for the really quick responses. Here is a use case example:

 int res = ftl::range(1, 4).map([](int a){
     return ftl::range(a, 4).map([a](int b){
         return std::make_tuple(a, b);
     });
 })
 .flat_map([](std::tuple<int, int> x){ return std::get<0>(x) * std::get<1>(x); })
 .sum();

 assert(res, 25);

ftl :: range(开始,结束)函数返回范围为 [begin,end)的惰性迭代器。

The ftl::range(begin, end) function returns a lazy iterator over the range [begin, end).

推荐答案

并不是C ++禁用了lambda本身的复制分配运算符,而是默认情况下将lambda对象中的成员保存为const,然后赋值运算符基本上什么也不能对其进行赋值,因此它不会生成。如果要让lambdas 将成员保留为const,则可以使用 [...](...)可变{...}

It's not that C++ disables the copy-assignment operator for lambda per-se, but that by default members in a lambda object are saved as const, and then the assignment operator can basically do nothing to assign to them, and so it is not generated. If you want lambdas to not hold members as const, you use the [...](...) mutable {...} syntax.

另一件事是,我不确定您分配lambda会得到什么。我的意思是,如果您要重用lambda类型(和功能)并将其简单地绑定到不同的变量,则您已经在使用漂亮的lambda捕获语法,并且也可能将其作为普通的函数对象。 将一种lambda分配给另一种是不可能的。这意味着当您按值持有lambda本身时,您将无法提供不同的lambda实现。

The other thing is that I'm not entirely sure what you get out of assigning lambdas. I mean, if you're going to re-use the lambda type (and functionality) and simply bind it to different variables, you're already working against the nice lambda capture syntax, and might as well have it be a normal function object. Assigning one type of lambda to another one is impossible. This means that you can not provide different lambda implementations when you hold the lambda itself by value.

如果这仍然是您想要的,我认为动态分配(例如,使用 unique_ptr )是公平的游戏。

If this is still what you're going for, I think dynamic allocation (e.g. using unique_ptr) is fair game.

如果您真的想避免使用它,则可以手动破坏和重构lambda,如下所示示例说明:

And if you really want to avoid it, you could manually destruct and re-construct your lambda, as the following example illustrates:

#include <iostream>

template <class T>
struct LambdaContainer {
    LambdaContainer(const T& lambda)
        : lambda{lambda} {}

    void resetLambda(const T& lambda) {
        this->lambda.~T();
        new (&this->lambda) T{lambda};
    }

    T lambda;
};

int main()
{
    int i = 1;

    auto l = [=]() {
        std::cout << i;
    };

    using LT = decltype(l);

    LambdaContainer<LT> lc{l};

    lc.resetLambda(l);
}

这篇关于在C ++中模拟Lambda的复制分配运算符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆