2016-04-27 300 views
7

我有一个接受函数作为参数的模板。如何将lambda表达式传递给C++模板作为参数

当我尝试传递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; 
} 

我失踪了?

+0

'function (1);'不是'ok'。模板需要一个类型或一个整数常量作为参数。 “测试”既不是。并且都不是'lambda'。 – DeiDei

+0

这是确定的,检查这个答案http://stackoverflow.com/questions/1174169/function-passed-as-template-argument/2156899#2156899,你也可以尝试它 – gsf

+0

lambda已经是std ::函数类型。你在做什么看起来像函数)>(1)这是无效的(因为1是一个int不是函数)。 – Striker

回答

11

lambda不是函数指针! lambda是编译器生成的类的实例!

int main() { 
    auto lambda = [](int a) { return a; }; 

    func ptr = +lambda; // this would work 

    return 0; 
} 

可悲的是,operator+甚至不会在你的情况下工作,因为它具有:

然而,非捕获拉姆达可以使用它的operator+

下面是一个例子转换为函数指针没有被声明为constexpr,所以你不能在模板参数中使用它。

对你的情况的修复将是使用免费功能...直到N4487不被接受,你不能指望将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文件时,它可能是有用的。

如果你的目标仅仅是编译器能够内联的代码,您可以使用模板来绕过拉姆达:

#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每个instanciation类型,好编译器应该能够优化出lambda。

铿锵,第三个选项提供了以下组件:

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作为标志。

+1

是内联函数是我想要的,让我试试第三种方法。 – gsf

2

我不知道这个标准是否足够说明这是我的编译器故障没有正确实现,还是实际上是标准的,但是在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; 
} 
+0

不是编译器的错。编译时lambda会希望成为C++ 17中的一件事! – DeiDei

+0

虽然这不是我想要的。我希望这是一个模板参数,所以函数最终被内联而不是指针总是被称为运行时。 – gsf

6

这是因为lambda作为它自己的类型。
您对传递的函数的类型有templatize 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); 
} 
相关问题