C++ lambdas 和模板的交互

Ken*_*lou 5 c++ lambda templates c++17

我正在尝试围绕 C++ lambda 表达式和模板的交互进行思考。

此代码按我的预期工作:

#include <iostream>

int bar (int x, int (* f) (int))
{
    return f (x);
}

double bar (double x, double (* f) (double))
{
    return f (x);
}

int main ()
{
    std::cout << bar (16, [] (int x) -> int { return x * x; }) << std::endl;
    std::cout << bar (1.2, [] (double x) -> double { return x * x; }) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这样做也是如此:

#include <iostream>
#include <functional>

int bar (int x, std::function<int (int)> f)
{
    return f (x);
}

double bar (double x, std::function<double (double)> f)
{
    return f (x);
}

int main ()
{
    std::cout << bar (16, [] (int x) -> int { return x * x; }) << std::endl;
    std::cout << bar (1.2, [] (double x) -> double { return x * x; }) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。但以下示例均无法编译:

#include <iostream>

template <typename T>
T bar (T x, T (* f) (T))
{
    return f (x);
}

int main ()
{
    std::cout << bar (16, [] (int x) -> int { return x * x; }) << std::endl;
    std::cout << bar (1.2, [] (double x) -> double { return x * x; }) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

#include <iostream>
#include <functional>

template <typename T>
T bar (T x, std::function <T (T)> f)
{
    return f (x);
}

int main ()
{
    std::cout << bar (16, [] (int x) -> int { return x * x; }) << std::endl;
    std::cout << bar (1.2, [] (double x) -> double { return x * x; }) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

GCC 版本 8.3.0(使用 -std=c++17)给出了一条错误消息:

no matching function for call to 'bar(int, main()::<lambda(int)>' (and
another for the "double" version) and "template argument
deduction/substitution failed: main()::<lambda(int)> is not derived
from std::function<T(T)>" (for the second failing example).
Run Code Online (Sandbox Code Playgroud)

但是,此任务有效:

std::function<int (int)> f = [] (int x) -> int { return x * x; };
Run Code Online (Sandbox Code Playgroud)

任何人都可以为我照亮吗?(显然,这不是实用的代码。这只是学习的尝试。)

son*_*yao 5

问题是std::function模板参数推导中不会考虑隐式转换(从 lambda 到函数指针或);然后在你的第三个和第四个样本类型推导中T,第二个函数参数(即 lambda)失败。

类型推导不考虑隐式转换(上面列出的类型调整除外):这是重载解析的工作,稍后会发生。

在这种情况下T,只能从第一个函数参数中推导出来;您可以使用非推导上下文声明第二个,以将其从推导中排除。

例如

template <typename T>
T bar (T x, std::type_identity_t<T (*) (T)> f)
{
    return f (x);
}

template <typename T>
T bar (T x, std::type_identity_t<std::function <T (T)>> f)
{
    return f (x);
}
Run Code Online (Sandbox Code Playgroud)

PS:std::type_identity从C++20开始就可以了,在此之前你可以自己做一个,不难。

居住


Dre*_*ann 5

您的失败代码在模板参数推导过程中需要函数指针,而 lambda 不是函数指针。

但是,像您这样的非捕获 lambda 可以转换为函数指针。

最简洁的方法是应用一元+。这+是有效的,因为 a对指针有效,在模板替换发生之前触发转换。

#include <iostream>

template <typename T>
T bar (T x, T (* f) (T))
{
    return f (x);
}

int main ()
{
    std::cout << bar (16, +[] (int x) -> int { return x * x; }) << std::endl;
//                        ^ this is the only change
    std::cout << bar (1.2, +[] (double x) -> double { return x * x; }) << std::endl;
//                         ^ this is the only change

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

看到它在 Godbolt 上编译


abh*_*ora 4

可能不是您真正需要的,但它可以使用C++11(因为Lambda 函数只是匿名函子的语法糖):

#include <iostream>

template <typename T, typename L>
T bar (T x, const L& l)
{
    return l (x);
}

int main ()
{
    std::cout << bar (16, [] (int x) -> int { return x * x; }) << std::endl;
    std::cout << bar (1.2, [] (double x) -> double { return x * x; }) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)