使用std :: function的模板并不直接匹配lambda

时间:2017-07-02 13:38:27

标签: c++ lambda c++14

如何最好地定义模板函数@以匹配lambda&#39签名?

我不想定义一个捕获整个函数类型的通用模板参数, 因为我想使用传入函数的参数类型applyFunc

E

编译失败:

#include <functional>
template<class E>
int applyFunc(std::function<int(E)> f) {
    return f(E{});   
}

int main()
{
    auto asLambda = [](int d) -> int {  return d+d; };
    std::function<int(int)> asFunc = asLambda;

    applyFunc(asLambda); //this doesn't :(
    applyFunc(asFunc); //this works
}

1 个答案:

答案 0 :(得分:1)

  

如何最好地定义模板函数applyFunc以匹配lambda的签名?

只要您接受仅使用非捕获lambdas(就像在示例代码中所做的那样),就可以利用它们衰减到函数指针的事实。
作为一个最小的工作示例:

template<class E>
int applyFunc(int(*f)(E)) {
    return f(E{});   
}

int main() {
    auto asLambda = [](int d) -> int {  return d+d; };
    applyFunc(+asLambda);
}

如果你想使用捕获lambdas,你可以以某种方式提取类型E如果你接受以下内容:

  • 您必须使用通用类型F而不是std::function
  • 您不能使用通用lambdas

然后,您可以直接查看您的lambda的operator() 它遵循一个最小的工作示例:

template<typename F>
struct GetFrom {
    template<typename R, typename E>
    static E typeE(R(F::*)(E) const);

    // required for mutable lambdas
    template<typename R, typename E>
    static E typeE(R(F::*)(E));
};

template<class F>
int applyFunc(F f) {
    using E = decltype(GetFrom<F>::typeE(&F::operator()));
    return f(E{});   
}

int main() {
    int i = 0;
    applyFunc([i](int d) mutable -> int {  return d+d; });
    applyFunc([i](int d) -> int {  return d+d; });
}

如果需要,您可以轻松地将其扩展为多个参数。使用std::tuple作为返回类型并从中获取第i种类型。

最后,如果你想使用捕获lambdas并因任何原因将它们分配给std::function,请注意E不能自动推断,因此你必须明确指定它(如@cpplearner对问题的评论:

applyFunc<int>([](int d) { return d+d; })

修改

GetFrom也可以直接在SFINAE表达式中使用,如评论中所要求的 作为一个最小的工作示例:

#include<type_traits>

template<typename F>
struct GetFrom {
    template<typename R, typename E>
    static E typeE(R(F::*)(E) const);

    // required for mutable lambdas
    template<typename R, typename E>
    static E typeE(R(F::*)(E));
};

template<class F, typename E = decltype(GetFrom<F>::typeE(&F::operator()))>
std::enable_if_t<std::is_same<E, int>::value, E>
applyFunc(F f) {
    return f(E{});   
}

template<class F, typename E = decltype(GetFrom<F>::typeE(&F::operator()))>
std::enable_if_t<std::is_same<E, double>::value, E>
applyFunc(F f) {
    return f(E{});   
}

int main() {
    int i = 0;
    applyFunc([i](int d) mutable -> int {  return d+d; });
    applyFunc([i](double d) -> int {  return d+d; });
    // this won't compile, as expected
    // applyFunc([i](char d) -> int {  return d+d; });
}