Cor*_*mer 186 c++ lambda function-pointers c++11
是否可以将lambda函数作为函数指针传递?如果是这样,我必须做错了,因为我收到编译错误.
请考虑以下示例
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我尝试编译它时,我得到以下编译错误:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
Run Code Online (Sandbox Code Playgroud)
这是一个消化错误消息,但我认为我得到的是lambda不能被视为一个constexpr因此我不能将它作为函数指针传递?我也试过制作xconst,但这似乎没有帮助.
Sha*_*our 186
如果没有捕获lambda只能转换为函数指针,从草案C++ 11标准部分5.1.2 [expr.prim.lambda]说(强调我的):
没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const 转换函数,用于指向具有与闭包类型的函数调用操作符相同的参数和返回类型的函数.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果.
注意,cppreference还在它们关于Lambda函数的部分中介绍了这一点.
所以以下替代方案将起作用:
typedef bool(*DecisionFn)(int);
Decide greaterThanThree{ []( int x ){ return x > 3; } };
Run Code Online (Sandbox Code Playgroud)
这样:
typedef bool(*DecisionFn)();
Decide greaterThanThree{ [](){ return true ; } };
Run Code Online (Sandbox Code Playgroud)
正如5gon12eder指出的那样,你也可以使用std::function,但要注意std::function重量很重,所以这不是一个无成本的权衡.
5go*_*der 85
Shafik Yaghmour的回答正确地解释了为什么lambda如果有捕获就不能作为函数指针传递.我想为这个问题展示两个简单的修复方法.
使用std::function而不是原始函数指针.
这是一个非常干净的解决方案.但请注意,它包含了类型擦除的一些额外开销(可能是虚函数调用).
#include <functional>
#include <utility>
struct Decide
{
using DecisionFn = std::function<bool()>;
Decide(DecisionFn dec) : dec_ {std::move(dec)} {}
DecisionFn dec_;
};
int
main()
{
int x = 5;
Decide greaterThanThree { [x](){ return x > 3; } };
}
Run Code Online (Sandbox Code Playgroud)使用不捕获任何内容的lambda表达式.
由于您的谓词实际上只是一个布尔常量,因此以下内容可以快速解决当前问题.请参阅此答案,以获得有关其原因和方法的详细解释.
// Your 'Decide' class as in your post.
int
main()
{
int x = 5;
Decide greaterThanThree {
(x > 3) ? [](){ return true; } : [](){ return false; }
};
}
Run Code Online (Sandbox Code Playgroud)Nox*_*xer 33
我知道这有点老了..
但我想补充一下:
Lambda表达式(甚至是捕获的表达式)可以作为函数指针处理!
这很棘手,因为Lambda表达式不是一个简单的函数.它实际上是一个带有operator()的对象.
当你有创意时,你可以使用它!想想std :: function风格的"函数"类.如果保存对象!
您也可以使用函数指针.
要使用函数指针,可以使用以下命令:
int first = 5;
auto lambda = [=](int x, int z) {
return x + z + first;
};
int(decltype(lambda)::*ptr)(int, int)const = &decltype(lambda)::operator();
std::cout << "test = " << (lambda.*ptr)(2, 3) << std::endl;
Run Code Online (Sandbox Code Playgroud)
要构建一个可以像"std :: function"一样开始工作的类,我将做一些简短的例子.首先,你需要一个类/结构,而不是存储对象和函数指针,你还需要一个operator()来执行它:
// OT => Object Type
// RT => Return Type
// A ... => Arguments
template<typename OT, typename RT, typename ... A>
struct lambda_expression {
OT _object;
RT(OT::*_function)(A...)const;
lambda_expression(const OT & object)
: _object(object), _function(&decltype(_object)::operator()) {}
RT operator() (A ... args) const {
return (_object.*_function)(args...);
}
};
Run Code Online (Sandbox Code Playgroud)
有了这个,你现在可以运行捕获的,非捕获的lambda,就像你使用原始的:
auto capture_lambda() {
int first = 5;
auto lambda = [=](int x, int z) {
return x + z + first;
};
return lambda_expression<decltype(lambda), int, int, int>(lambda);
}
auto noncapture_lambda() {
auto lambda = [](int x, int z) {
return x + z;
};
return lambda_expression<decltype(lambda), int, int, int>(lambda);
}
void refcapture_lambda() {
int test;
auto lambda = [&](int x, int z) {
test = x + z;
};
lambda_expression<decltype(lambda), void, int, int>f(lambda);
f(2, 3);
std::cout << "test value = " << test << std::endl;
}
int main(int argc, char **argv) {
auto f_capture = capture_lambda();
auto f_noncapture = noncapture_lambda();
std::cout << "main test = " << f_capture(2, 3) << std::endl;
std::cout << "main test = " << f_noncapture(2, 3) << std::endl;
refcapture_lambda();
system("PAUSE");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此代码适用于VS2015希望它有用:)
映入眼帘!
编辑:删除针模板FP,删除函数指针参数,重命名为lambda_expression
更新04.07.17:
template <typename CT, typename ... A> struct function
: public function<decltype(&CT::operator())(A...)> {};
template <typename C> struct function<C> {
private:
C mObject;
public:
function(const C & obj)
: mObject(obj) {}
template<typename... Args> typename
std::result_of<C(Args...)>::type operator()(Args... a) {
return this->mObject.operator()(a...);
}
template<typename... Args> typename
std::result_of<const C(Args...)>::type operator()(Args... a) const {
return this->mObject.operator()(a...);
}
};
namespace make {
template<typename C> auto function(const C & obj) {
return ::function<C>(obj);
}
}
int main(int argc, char ** argv) {
auto func = make::function([](int y, int x) { return x*y; });
std::cout << func(2, 4) << std::endl;
system("PAUSE");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Pas*_* By 13
正如这个答案指出的那样,捕获lambda不能转换为函数指针.
但是,向只接受一个API的API提供函数指针通常会非常麻烦.最经常引用的方法是提供一个函数并用它调用一个静态对象.
static Callable callable;
static bool wrapper()
{
return callable();
}
Run Code Online (Sandbox Code Playgroud)
这很乏味.我们进一步采用这一想法,使创建过程更加自动化wrapper,让生活变得更加轻松.
#include<type_traits>
#include<utility>
template<typename Callable>
union storage
{
storage() {}
std::decay_t<Callable> callable;
};
template<int, typename Callable, typename Ret, typename... Args>
auto fnptr_(Callable&& c, Ret (*)(Args...))
{
static bool used = false;
static storage<Callable> s;
using type = decltype(s.callable);
if(used)
s.callable.~type();
new (&s.callable) type(std::forward<Callable>(c));
used = true;
return [](Args... args) -> Ret {
return Ret(s.callable(std::forward<Args>(args)...));
};
}
template<typename Fn, int N = 0, typename Callable>
Fn* fnptr(Callable&& c)
{
return fnptr_<N>(std::forward<Callable>(c), (Fn*)nullptr);
}
Run Code Online (Sandbox Code Playgroud)
并用它作为
void foo(void (*fn)())
{
fn();
}
int main()
{
int i = 42;
auto fn = fnptr<void()>([i]{std::cout << i;});
foo(fn); // compiles!
}
Run Code Online (Sandbox Code Playgroud)
这基本上是在每次出现时声明一个匿名函数fnptr.
请注意,调用fnptr覆盖以前写入callable的相同类型的给定callables.我们在一定程度上使用int参数来解决这个问题N.
std::function<void()> func1, func2;
auto fn1 = fnptr<void(), 1>(func1);
auto fn2 = fnptr<void(), 2>(func2); // different function
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
118864 次 |
| 最近记录: |