【问题标题】:Interaction of C++ lambdas and templates [duplicate]C ++ lambdas和模板的交互[重复]
【发布时间】:2020-05-06 03:25:38
【问题描述】:

我正试图围绕 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;
}

也是这样:

#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;
}

到目前为止,一切都很好。但以下示例均无法编译:

#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;
}

#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;
}

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

但是,这个任务有效:

std::function<int (int)> f = [] (int x) -> int { return x * x; };

谁能帮我解释一下? (显然,这不是实用代码。这只是一个学习尝试。)

【问题讨论】:

    标签: c++ templates lambda c++17


    【解决方案1】:

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

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

    在这种情况下,T 只能从第一个函数参数推导出;您可以使用non-deduced context 声明第二个,以将其排除在扣除之外。

    例如

    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);
    }
    

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

    LIVE

    【讨论】:

    • 谢谢。我不知道在 C++20 中添加了 std::type_identity_t。 (我承认我还没有学过 C++20。)我担心 C++ 的重量比我在大流行隔离中更快! :-)
    【解决方案2】:

    您的失败代码在模板参数推导期间需要 函数指针,而 lambdas 不是函数指针。

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

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

    #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;
    }
    

    See it compile on godbolt

    【讨论】:

    • 谢谢。我不得不承认,如果我看到这段代码在 lambda 前面带有一元加号,我将不知道发生了什么。
    【解决方案3】:

    可能不是您真正需要的,但它适用于C++11(如Lambda functions are just syntactic sugar for anonymous functors):

    #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;
    }
    

    【讨论】:

    • 不错!我认为这是最干净的解决方案。
    【解决方案4】:

    以防万一,不要忘记这也有效:

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

    【讨论】:

    • 是的,当然,这是对“如何防止类型推断中的问题?”问题的一种可能答案。不要使用它。 ;-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多