如何表示和传递C ++ 11 lambdas? [英] How are C++11 lambdas represented and passed?

查看:123
本文介绍了如何表示和传递C ++ 11 lambdas?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在c ++ 11中传递lambda非常容易:

  func([ $ b // code 
});

但我想知道,传递lambda的费用是多少到这样的功能?如果func将lambda传递给其他函数,该怎么办?

  void func(function< void b $ b doSomethingElse(f); 
}

lambda的传递是否昂贵?由于函数对象可以赋值为0,

  function< void(int arg)> f = 0; // 0表示不是init

它让我认为函数对象的行为像指针。但是没有使用 new ,那么它们意味着它们可能类似于值类型 struct 或类,它默认为stack



当你按value传递一个函数对象时,如何传递一个C ++ 11代码主体和一组捕获的变量?有很多的代码体的多余的副本吗?我必须标记通过 const& 传递的每个函数对象,以便不进行复制:

  void func(const function< void(int arg)>& f){
}



或者函数对象以不同于常规C ++结构的方式传递?

免责声明:我的答案有点简化了与现实(我把一些细节放在一边),但大的图景在这里。此外,标准没有完全指定如何lambdas或 std :: function 必须在内部实现(实现有一些自由),所以,像任何讨论的实现细节,你的编译器可能也可能不会这样做。



但是,这是一个非常类似于VTables的主题:标准不需要太多,但任何明智的编译器仍然很可能这样做,所以我相信它值得挖一点。 :)






Lambdas



一个lambda是一种匿名的 struct

  auto lambda = ](Args ...) - >返回{/*...*/}; 

//大致相当于:
struct {
返回operator()(Args ...){/*...*/}
}
lambda; //匿名结构的实例

就像任何其他类一样,当你传递它的实例时






按值复制的对象将被复制到 struct

  
auto lambda = [=](Args ...) - >返回{/ * ... use v,captured by value ... * /};

//大致相当于:
struct Temporary {//注意:我们不能使它成为一个匿名结构,因为我们需要
//一个构造函数,但是只是一个语法quirk

const Value v; //注意:除非lambda是可变的,否则默认值为const。
临时(值v_):v(v_){}
返回operator()(Args ...){/ *。 ..使用v,捕获的值... * /}
}
lambda(v); // struct的实例

同样,传递它只意味着你传递数据






同样,通过引用捕获的对象被引用到 struct

  
auto lambda = [&](Args ...) - >返回{/ * ...使用v,通过引用捕获... * /};

//大致相当于:
struct Temporary {
Value& v; //注意:捕获通过引用是非常量
临时(值& v_):v(v_){}
返回operator()(Args ...){/ * ... use v ,通过引用捕获... * /}
}
lambda(v); // struct的实例

这是几乎所有当谈到lambdas本身






std: :function



std :: function 是一个通用的包装函数, (lambdas,独立/静​​态/成员函数,函子类,像我所展示的,...)。



std ::函数非常复杂,因为它们必须支持所有这些情况。根据函数的确切类型,这需要至少以下数据(给出或者获取实现细节):




  • 指向独立/静态函数。






  • 指向函数的副本 [见下面的注释] 的指针(动态分配以允许任何类型的函子,正如你所指出的)。


  • 一个指向分配器的指针,它能够复制函子和函数本身(因为可以使用任何类型的函子,指针函数应该是 void * ,因此必须有这样的机制 - 可能使用多态性aka基类+虚方法,派生类在本地生成 template 构造函数)。



事先知道它必须存储哪种函数(并且这可以通过 std :: function 可以重新分配的事实而变得明显),那么它必须处理所有可能的

我不知道 标准规定了它,但是在运行时会做出决定。 这肯定是一个新的副本,底层函数不共享:

  int v = 0; 
std :: function< void()> f = [=]()mutable {std :: cout< v ++<< std :: endl; };
std :: function< void()> g = f;

f(); // 0
f(); // 1
g(); // 0
g(); // 1

因此,当你传递一个 std :: function 周围至少涉及至少这四个指针(并且实际上在GCC 4.7 64位 sizeof(std :: function< void()> 四个64位指针)和可选的函数的动态分配副本(正如我已经说过,只包含捕获的对象,你不复制代码)。






回答问题




是将lambda传递给这样的函数的代价? [问题的上下文:按值]


< blockquote>

好吧,你可以看到它主要取决于你的函数(或者是一个手工的 struct 函数或lambda)与直接传递一个结构函数的值相比,开销是可以忽略不计的,但它当然比传递a struct 函数作为引用。


通过 const& 传递的函数对象不能创建副本?



$ b b

恐怕这很难用通用的方式回答。有时你需要通过 const 引用,有时通过值,有时通过 rvalue 引用,以便可以移动它。它真的取决于你的代码的语义。



你应该选择哪一个规则是一个完全不同的主题IMO,只是记住,他们是相同的任何其他对象。



无论如何,你现在有所有的键做出明智的决定(再次,取决于你的代码和语义)。


Passing a lambda is really easy in c++11:

func( []( int arg ) {
  // code
} ) ;

But I'm wondering, what is the cost of passing a lambda to a function like this? What if func passes the lambda to other functions?

void func( function< void (int arg) > f ) {
  doSomethingElse( f ) ;
}

Is the passing of the lambda expensive? Since a function object can be assigned 0,

function< void (int arg) > f = 0 ; // 0 means "not init" 

it leads me to think that function objects kind of act like pointers. But without use of new, then it means they might be like value-typed struct or classes, which defaults to stack allocation and member-wise copy.

How is a C++11 "code body" and group of captured variables passed when you pass a function object "by value"? Is there a lot of excess copy of the code body? Should I have to mark each function object passed with const& so that a copy is not made:

void func( const function< void (int arg) >& f ) {
}

Or do function objects somehow pass differently than regular C++ structs?

解决方案

Disclaimer: my answer is somewhat simplified compared to the reality (I put some details aside) but the big picture is here. Also, the Standard does not fully specify how lambdas or std::function must be implemented internally (the implementation has some freedom) so, like any discussion on implementation details, your compiler may or may not do it exactly this way.

But again, this is a subject quite similar to VTables: the Standard doesn't mandate much but any sensible compiler is still quite likely to do it this way, so I believe it is worth digging into it a little. :)


Lambdas

The most straightforward way to implement a lambda is kind of an anonymous struct:

auto lambda = [](Args...) -> Return { /*...*/ };

// roughly equivalent to:
struct {
    Return operator ()(Args...) { /*...*/ }
}
lambda; // instance of the anonymous struct

Just like any other class, when you pass its instances around you never have to copy the code, just the actual data (here, none at all).


Objects captured by value are copied into the struct:

Value v;
auto lambda = [=](Args...) -> Return { /*... use v, captured by value...*/ };

// roughly equivalent to:
struct Temporary { // note: we can't make it an anonymous struct any more since we need
                   // a constructor, but that's just a syntax quirk

    const Value v; // note: capture by value is const by default unless the lambda is mutable
    Temporary(Value v_) : v(v_) {}
    Return operator ()(Args...) { /*... use v, captured by value...*/ }
}
lambda(v); // instance of the struct

Again, passing it around only means that you pass the data (v) not the code itself.


Likewise, objects captured by reference are referenced into the struct:

Value v;
auto lambda = [&](Args...) -> Return { /*... use v, captured by reference...*/ };

// roughly equivalent to:
struct Temporary {
    Value& v; // note: capture by reference is non-const
    Temporary(Value& v_) : v(v_) {}
    Return operator ()(Args...) { /*... use v, captured by reference...*/ }
}
lambda(v); // instance of the struct

That's pretty much all when it comes to lambdas themselves (except the few implementation details I ommitted, but which are not relevant to understanding how it works).


std::function

std::function is a generic wrapper around any kind of functor (lambdas, standalone/static/member functions, functor classes like the ones I showed, ...).

The internals of std::function are pretty complicated because they must support all those cases. Depending on the exact type of functor this requires at least the following data (give or take implementation details):

  • A pointer to a standalone/static function.

Or,

  • A pointer to a copy[see note below] of the functor (dynamically allocated to allow any type of functor, as you rightly noted it).
  • A pointer to the member function to be called.
  • A pointer to an allocator that is able to both copy the functor and itself (since any type of functor can be used, the pointer-to-functor should be void* and thus there has to be such a mechanism -- probably using polymorphism aka. base class + virtual methods, the derived class being generated locally in the template<class Functor> function(Functor) constructors).

Since it doesn't know beforehand which kind of functor it will have to store (and this is made obvious by the fact that std::function can be reassigned) then it has to cope with all possible cases and make the decision at runtime.

Note: I don't know where the Standard mandates it but this is definitely a new copy, the underlying functor is not shared:

int v = 0;
std::function<void()> f = [=]() mutable { std::cout << v++ << std::endl; };
std::function<void()> g = f;

f(); // 0
f(); // 1
g(); // 0
g(); // 1

So, when you pass a std::function around it involves at least those four pointers (and indeed on GCC 4.7 64 bits sizeof(std::function<void()> is 32 which is four 64 bits pointers) and optionally a dynamically allocated copy of the functor (which, as I already said, only contains the captured objects, you don't copy the code).


Answer to the question

what is the cost of passing a lambda to a function like this?[context of the question: by value]

Well, as you can see it depends mainly on your functor (either a hand-made struct functor or a lambda) and the variables it contains. The overhead compared to directly passing a struct functor by value is quite negligible, but it is of course much higher than passing a struct functor by reference.

Should I have to mark each function object passed with const& so that a copy is not made?

I'm afraid this is very hard to answer in a generic way. Sometimes you'll want to pass by const reference, sometimes by value, sometimes by rvalue reference so that you can move it. It really depends on the semantics of your code.

The rules concerning which one you should choose are a totally different topic IMO, just remember that they are the same as for any other object.

Anyway, you now have all the keys to make an informed decision (again, depending on your code and its semantics).

这篇关于如何表示和传递C ++ 11 lambdas?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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