2016-12-28 158 views
1

考虑以下几点:如何用lambda函数作为参数传递模板函数中的函数类型参数?

#include <utility> 
#include <string> 

template<typename> 
class C; 

template<typename R, typename T> 
class C<R(&)(T)> { 
public: 
    template<typename F> 
    C(F&& fun) {} 
}; 

template<typename T> 
C<T> makeC(T&& fun) { 
    return C<T>(std::forward<T>(fun)); 
} 

int foo(int a){return a;} 

int main() { 
    auto p1 = makeC(foo); // OK 
    auto p2 = C<int(&)(int)>([](int a){return a;}); // OK 
    // auto p3 = makeC([](int a){return a;}); // FAIL 
} 

Live example

,因为编译器不能推断这是作为参数传递的拉姆达类型int(&)(int)p3声明失败。 p1是可以的,因为类型可以很容易地从函数foo得到,p2也可以,因为类型是显式声明的。

它失败:

error: invalid use of incomplete type 'class C<main()::<lambda(int)> >'

有什么办法使编译器corretly推断函数类型,给出一个lambda?

P.S .: C++ 17答案也可以,如果适用的话。

+1

http://stackoverflow.com/questions/21657627/what-is-the-type-signature-of-a-c11-1y-lambda-function – LogicStuff

回答

1

实际的问题是,lambda函数有它自己的类型,不能简化为R(&)(T)。因此,C<T>是一种不完整的类型,正如编译器正确列出的那样。


只要你使用非捕获Lambda表达式,你可以依赖他们衰减到函数指针,做这样一个事实:

auto p3 = makeC(*(+[](int a){return a;})); 

或者这样:

template<typename T> 
auto makeC(T&& fun) -> C<decltype(*(+std::forward<T>(fun)))> { 
    return C<decltype(*(+std::forward<T>(fun)))>(std::forward<T>(fun)); 
} 

捕捉lambdas的另一种可能的解决方案是:

#include <utility> 
#include <string> 

template<typename T> 
class C: T { 
    template<typename F> 
    C(F&& fun): T{std::forward<F>(fun)} {} 
}; 

template<typename R, typename T> 
class C<R(&)(T)> { 
public: 
    template<typename F> 
    C(F&& fun) {} 
}; 

template<typename T> 
C<T> makeC(T&& fun) { 
    return C<T>(std::forward<T>(fun)); 
} 

int foo(int a){return a;} 

int main() { 
    auto p1 = makeC(foo); 
    auto p2 = C<int(&)(int)>([](int a){return a;}); 
    auto p3 = makeC([](int a){return a;}); 
} 

这样,当处理一个lambda时,C实际上继承自它并且私下包含包含它的operator()

+1

很好的答案,谢谢!您的最终解决方案效果很好,但不幸的是,我需要能够区分模板实例中的lambda参数类型。我想我会坚持只支持不捕获lambda,因为在我的场景中它不是一个可怕的限制。 –

相关问题