如何使用作为参数传递的lambda推断模板函数中的函数类型参数?

时间:2016-12-28 11:45:57

标签: c++ templates lambda c++14 type-inference

请考虑以下事项:

#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

p3的声明失败,因为编译器无法从作为参数传递的lambda推断类型int(&)(int)p1是可以的,因为类型可以很容易地从函数foo中提取,而p2是可以的,因为显式声明了类型。

失败了:

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

有没有办法让编译器在给定lambda?

的情况下正确地推断出函数类型
  

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

1 个答案:

答案 0 :(得分:1)

实际问题是lambda函数有自己的类型,不能简化为R(&)(T)。因此,C<T>是编译器正确概述的不完整类型。

只要您使用非捕获lambdas,就可以依赖它们衰变为函数指针的事实并执行此操作:

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()