如何在 C++11 中传递和执行匿名函数作为参数?
我正在寻找的代码如下.
The code I'm looking for is like following.
bool Func1(int Arg1, C++11LambdaFunc Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
}
}
稍后我将使用此代码.
Func1(12, [](int D) -> bool { ... } );
推荐答案
基础版本,用于头文件:
Basic version, for use in a header file:
template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
更复杂的版本,如果你想从你的实现中分离你的界面(它有运行时间成本):
More complex version, if you want to split your interface from your implementation (it has run time costs):
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
std::function
使用类型擦除在 lambda 周围创建自定义创建的包装器,然后公开一个使用 pImpl
模式转发的非虚拟接口它到自定义创建的包装器.1
std::function
uses type erasure to create a custom-created wrapper around your lambda, and then exposes a non-virtual interface that uses the pImpl
pattern to forward it to the custom-created wrapper.1
或者,用不太专业的术语来说,std::function
是一个类,它可以包装几乎任何可以像函数一样调用的东西,传递一个兼容的参数传递一个 int
,它返回一些与返回 bool
兼容的东西.
Or, in less technical terms, std::function<bool(int)>
is a class that can wrap nearly anything that you can call like a function, passing one parameter that is compatible with passing an int
, and it returns something that is compatible with returning a bool
.
通过 std::function
调用的运行时间成本大致等于 virtual
函数调用(由上述类型擦除引起),并且当您创建它必须复制传入的函数对象(又名函子)的状态(这可能很便宜――无状态的 lambdas,或者 lambdas 通过引用捕获参数――或者在其他一些情况下很昂贵)并存储它(通常在免费的存储或堆,这有成本),而纯模板版本可以内联".在调用点(即,不仅可以比函数调用花费更少,编译器甚至可以优化函数调用和返回边界!)
A call through a std::function
has a run time cost roughly equal to a virtual
function call (caused by the above type erasure), and when you create it it has to copy the state of the function object (aka functor) passed in (which can be cheap -- stateless lambdas, or lambdas capturing arguments by reference -- or expensive in some other cases) and store it (typically on the free store or heap, which has a cost), while the pure-template versions can be "inlined" at the point of call (ie, can not only cost less than a function call, the compiler can even optimize over the function call and return boundaries!)
如果你想在没有 std::function
的所有运行时成本的情况下拆分接口/实现,你可以滚动你自己的 function_ref(在 c++17,因为这样可以减少一些样板文件):>
If you want to split interface/implementation without all of the runtime costs of std::function
, you can roll your own function_ref (in c++17, because that cuts down on some boilerplate):
template<class Sig>
struct function_ref;
template<class R, class...Args>
struct function_ref<R(Args...)> {
R operator()(Args...args) const {
return pf(state, std::forward<Args>(args)...);
}
function_ref()=default;
function_ref(function_ref const&)=default;
function_ref& operator=(function_ref const&)=default;
explicit operator bool()const{ return pf!=nullptr; }
// this overload reduces indirection by 1 step
// and allows function_ref<Sig> to resolve overloads
// on an overload set sometimes.
function_ref( R(*f)(Args...) ):
pf([](State const& state, Args&&...args)->R{
return reinterpret_cast<R(*)(Args...)>(state.pfunstate)(std::forward<Args>(args)...);
})
{
state.pfunstate = reinterpret_cast<void(*)()>(f);
}
// this grabs anything callable (that isn't this own type)
// and stores a pointer to it to call later.
template<class F>
requires (
std::is_convertible_v<
std::invoke_result_t< std::remove_reference_t<F>, Args... >, R
>
&& !std::is_same_v< std::decay_t<F>, function_ref >
)
function_ref( F&& f ):
pf([](State const& state, Args&&...args)->R{
return (*(std::remove_reference_t<F>*)state.pstate)(std::forward<Args>(args)...);
})
{
state.pstate = std::addressof(f);
}
private:
union State {
void* pstate = nullptr;
void(*pfunstate)();
};
State state;
R(*pf)(State const&, Args&&...) = nullptr;
};
// a deduction guide permitting function_ref{foo} to work
// if foo is a non-overloaded function name.
template<class R, class...Args>
function_ref( R(*)(Args...) )->function_ref<R(Args...)>;
实例.
这消除了从 std::function
中进行任何分配的需要,方法是从中删除所有权语义并仅进行类型擦除调用.
This removes the need to ever do any allocation from std::function
by removing ownership semantics from it and just type-erasing calling.
第一个示例的奇特版本,它也可以更好地处理一些极端情况:(也必须在头文件中实现,或者在使用时在同一个翻译单元中)
A fancy version of the first example that also handles some corner cases a tad better: (also must be implemented within a header file, or in the same translation unit as it is used)
template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
if(Arg1 > 0){
return std::forward<Lambda>(Arg2)(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
它使用一种称为完美转发"的技术.对于某些函子,这会产生与 #1 略有不同的行为(通常是更正确的行为).
which uses a technique known as "perfect forwarding". For some functors, this generates slightly different behavior than #1 (and usually more correct behavior).
大部分改进来自在参数列表中使用 &&
:这意味着传递了对函子的引用(而不是副本),从而节省了一些成本,并允许传入 const
或非 const
函子.
Most of the improvement comes form the use of &&
in the argument list: this means that a reference to the functor is passed in (instead of a copy), saving some costs, and allows both a const
or non-const
functor to be passed in.
std::forward<Lambda>(...)
更改只会在有人使用允许方法(包括 operator()
) 以覆盖 this
指针的右值/左值状态.理论上,这可能很有用,但我见过的基于 this
的右值状态实际覆盖的函子数量是 0
.当我在编写严肃的库代码 (tm) 时,我会为此烦恼,但很少会这样做.
The std::forward<Lambda>(...)
change would only cause a change in behavior if someone used a relatively new C++ feature that allows methods (including operator()
) to override on the rvalue/lvalue status of the this
pointer. In theory, this could be useful, but the number of functors I've seen that actually override based on the rvalue status of this
is 0
. When I'm writing serious library code (tm) I go to this bother, but rarely otherwise.
还有一件事需要考虑.假设你想要一个返回 bool
的函数,或者一个返回 void
的函数,如果函数返回 void
你想要处理就好像它返回了 true
.例如,您正在使用一个在迭代某个集合时被调用的函数,并且您希望有选择地支持提前停止.该函数在想要提前停止时返回 false
,否则返回 true
或 void
.
There is one more possible thing to consider. Suppose you want to take either a function that returns bool
, or a function that returns void
, and if the function returns void
you want to treat it as if it returned true
. As an example, you are taking a function that is being called when iterating over some collection, and you want to optionally support early halting. The function returns false
when it wants to stop prematurely, and true
or void
otherwise.
或者,在更一般的情况下,如果您对一个函数进行了多次覆盖,其中一个采用一个函数,而另一些在同一位置采用其他类型.
Or, in a more general case, if you have multiple overrides of a function, one of which takes a function and others take some other type at the same location.
这是可能的,就我要进入的范围而言(使用智能适配器或通过 SFINAE 技术).但是,您最好只创建两个不同的命名函数,因为所需的技术太重了.
This is possible, which is as far as I'm going to get into here (either with a smart adapter, or via SFINAE techniques). However, you are probably better off just creating two different named functions, because the techniques required are way too heavy weight.
1 从技术上讲,std::function
可以使用魔法仙尘来做它所做的事情,因为它的行为是由标准描述的,而不是它的实现.我正在描述一个简单的实现,它近似于我与之交互的 std::function
实现的行为.
1 Technically std::function
could use magic fairy dust to do what it does, as its behavior is described by the standard, and not its implementation. I'm describing a simple implementation that approximates the behavior of the std::function
implementation I have interacted with.
相关文章