且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

C ++***做法:通过const引用或转发引用(也称为通用引用)将仅使用(未存储的)lambda参数传递给函数

更新时间:2023-11-12 17:22:10

如果您试试您可以看到,采用不同lambda的方式,const引用不能采用可变的lambda,而可变引用不能采用临时的(因此您不能在现场定义lambda).

对所有类型的lambda值均采用取值和通用参考值取值.但是通用引用是更通用的(对双关语):如果您有要使用多次的lambda,但没有复制构造函数,则不能按价值来接受它,因为您必须移动它并且不能(或至少不应)在那之后使用它.

总而言之,我认为通用引用是最简单的方法.

附录:因为您在注释中询问了性能:如果通过转发引用传递传递值或通过值传递传递差异很大,则可能是您做错了.功能对象通常应轻巧.如果您的lambda需要花费大量精力来传递价值,则应该重新设计它.

即使您可以使用转发引用等使它起作用,这似乎也很麻烦,您很容易犯错并且进行昂贵的复制操作.即使您绝对正确地执行了操作,并且从来没有在您的代码中包含副本,但仍有两个地方可能发生副本:

  • 其他人的代码:大多数人认为功能对象是轻量级的,因此按值传递它们没有问题.
  • STL:看一下算法头.在那里传递的所有函数对象都按值取值(因为STL还假定函数对象是轻量级的).因此,如果您在任何地方都使用带有STL算法的繁重代码(众所周知,我们将在任何可能的地方使用它们)将使您陷入困境.

What is the best (i.e. most performant, most versatile) way to pass a lambda function as a parameter to a function which only uses (but does not store or forward) the lambda function?

Option 1: Is passing it by const-reference the way to go?

template <typename F>
void just_invoke_func(const F& func) {
    int i = 1;
    func(i);
}

Option 2: Or is it better to pass it as a forwarding reference (universal reference)?

template <typename F>
void just_invoke_func(F&& func) {
    int i = 1;
    func(i);
}

Does the latter have any advantage over the former?
Are there any hazards with one of the solutions?

Edit #1:

Option 3: As pointed out by Yakk - Adam Nevraumont, mutable lambdas would not work with option 1. So, what about another version that takes a non-const l-value reference:

template <typename F>
void just_invoke_func(F& func) {
    int i = 1;
    func(i);
}

The question is: Does option 2 have any advantage over option 3?

Edit #2:

Option 4: Another version that takes the lambda by value has been proposed.

template <typename F>
void just_invoke_func(F func) {
    int i = 1;
    func(i);
}

If you try the different ways of taking different lambdas, you can see, that const references cannot take mutable lambdas and mutable reference cannot take temporaries (so you cannot define the lambda on the spot).

Both taking by value and taking by universal reference work for all types of lambdas. But the universal reference is more universal (pardon the pun): If you have a lambda that you want to use multiple times but that has no copy-constructor, you cannot take it by value, since you would have to move it and cannot (or at least should not) use it after that.

All in all, I think universal reference is the easiest way to go.

ADDENDUM: Because you asked about performance in the comments: If passing by forwarding reference vs. by value makes a huge difference, you are probably doing something wrong. Function objects should generally be light-weight. If you have a lambda that takes significant effort to pass by value, you should redesign that.

Even if you can make it work using forwarding references and so on, this seems to be a major hassle and you can easily make a mistake and have costly copy operations. Even if you do that absolutely right and never have copies in your code, there are two places where they can happen:

  • other people's code: Most people think of function objects a light-weight and therefore have no problems with passing them by value.
  • The STL: Take a look at the algorithms header. All function objects that are passed there are taken by value (because the STL also assumes function objects to be light-weight). So if you use a heavy one anywhere with STL-algorithms (and as we all know: We should use them, whereever we can) you are in for a bad time.