如何将 lambda 表达式作为参数传递给 c++ 模板
Posted
技术标签:
【中文标题】如何将 lambda 表达式作为参数传递给 c++ 模板【英文标题】:How I can pass lambda expression to c++ template as parameter 【发布时间】:2016-08-21 05:14:43 【问题描述】:我有一个接受函数作为参数的模板。
当我尝试传递 lambda 表达式时,它不会编译。
typedef int (*func)(int a);
template <func foo>
int function(int a)
foo(a);
int test(int a)
return a;
int main()
function<test>(1); // ---> this is ok
auto lambda = [](int a) -> int return a; ;
function<lambda>(1); // ---> this is wrong, why?
return 0;
我错过了什么?
【问题讨论】:
function<test>(1);
不是ok
。模板需要类型或整数常量作为参数。 test
两者都不是。 lambda
也不是。
没关系,看看这个答案***.com/questions/1174169/…你也可以试试
@Striker 不,为什么 lambda 会是 std::function
? lambda 的类型只是 operator()
重载的唯一类。
@Striker 是的,这样你就把它放在std::function
中,而用auto
声明它不是。
@Striker 如果你这样做,你会在其构造函数中构造一个带有 lambda 的 std::function
,并且 std::function 具有相当显着的运行时开销。当您需要运行时类型时,它很有用。说 lambda 是 std::function
类型就像说字符串文字是 std::string
。他们不是一回事。
【参考方案1】:
lambda 不是函数指针! lambda 是编译器生成的类的实例!
但是,非捕获 lambda 可以使用 operator+
转换为函数指针
这是一个例子:
int main()
auto lambda = [](int a) return a; ;
func ptr = +lambda; // this would work
return 0;
遗憾的是,operator+
在您的情况下甚至都不起作用,因为它尚未声明为 constexpr,因此您不能在模板参数中使用它。
解决您的情况的方法是使用免费函数...在不接受 N4487 之前,您不能期望将 lambda 作为模板参数传递。
另一种解决方法是创建自己的仿函数而不是 lambda:
struct LambdaType
constexpr LambdaType() = default;
int operator()(int a)
return run(a);
// this is a non-capturing lambda, the operator can be
// in a static function
static int run(int a)
return a;
;
int main()
LambdaType lambda;
function<&LambdaType::run>(1); // ---> this is working
return 0;
这个解决方案不是很吸引人,但如果 LambdaType
隐藏在 cpp 文件中,它可能会很有用。
如果您的目标只是编译器能够内联您的代码,您可以使用模板来传递 lambda:
#include <iostream>
template <typename T>
int function(T foo, int a)
return foo(a);
int main()
int a;
std::cin >> a;
int b = function([](int a) return a; , a);
return b;
由于编译器知道每个实例化的T
的类型,一个好的编译器应该能够优化出 lambda。
使用 clang,第三个选项给出以下程序集:
main: # @main
pushq %rax
leaq 4(%rsp), %rsi
movl std::cin, %edi
callq std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
movl 4(%rsp), %eax # this is the call to the function
addq $8, %rsp
retq
pushq %rax
movl std::__ioinit, %edi
callq std::ios_base::Init::Init()
movl std::ios_base::Init::~Init(), %edi
movl std::__ioinit, %esi
movl $__dso_handle, %edx
popq %rax
jmp __cxa_atexit # TAILCALL
我使用-std=c++14 -Ofast -march=native
作为标志。
【讨论】:
是的,内联函数就是我想要的,让我试试第三个选项。【参考方案2】:这是因为 lambda 作为自己的类型。
您已经对传递的函数类型模板化function()
。
template<typename F>
int function(F foo, int a)
return foo(a);
int test(int a)
return a;
int main()
// function will work out the template types
// based on the parameters.
function(test, 1);
function([](int a) -> int return a; , 1);
【讨论】:
【参考方案3】:我对标准的了解不够多,无法说明这是我的编译器错误没有正确实现它还是它实际上是标准,但是使用 VS2015 你无法生成编译时常量 lambda 表达式。模板只需要编译时常量,所以没有 lambdas。
但是,如果您想传递一个 lambda,您不需要 将它作为模板。完全有可能没有:
#include <functional>
int function(std::function<int(int)> const& f, int a)
f(a);
int test(int a)
return a;
int main()
auto lambda = [](int a) -> int return a; ;
function(test, 1);
function(lambda, 1);
return 0;
【讨论】:
不是编译器的错。编译时 lambda 有望成为 C++17 中的一员! 但这不是我想要的。我希望这是一个模板参数,以便最终内联函数而不是始终调用运行时的指针。【参考方案4】:这只是因为“您不能使用局部变量的名称或地址作为模板参数。”。 如果要制作全局静态 lambda,请参阅 http://pfultz2.com/blog/2014/09/02/static-lambda/ 你可能会找到你想要的。
【讨论】:
以上是关于如何将 lambda 表达式作为参数传递给 c++ 模板的主要内容,如果未能解决你的问题,请参考以下文章
将 lambda 函数作为第三个参数传递给 QObject::connect 时出错