为什么在传递lambda而不是功能指针时不能推断模板参数

Why could not deduce template argument when passing lambda instead of function pointer

本文关键字:不能 参数 指针 功能 lambda 为什么      更新时间:2023-10-16

i具有一个泡泡函数,该功能采用数组,比较函数和一个布尔值,该功能指示是否应颠倒地对数组进行分类。这是一个支持任何数据类型的模板函数,并将自动推断数组大小。

指定比较函数时,如果我通过功能指针,编译器将自动推导数组的数据类型,这很棒。但是,如果我通过lambda,它不会自动推导。我必须明确指定数据类型,或static_cast lambda为fnCompare_t<double>

背后的原因是什么?因为根据这篇文章,只要lambda没有捕获,它就可以像纯老功能指针一样使用,但似乎并非总是如此?在这种情况下怎么会有所不同?

#include <iostream>
using namespace std;
template <typename T>
using fnCompare_t = int(*)(T const &, T const &);
template <typename T, size_t count>
inline void BubbleSort(
    T(&array)[count],
    fnCompare_t<T> fnCompare,
    bool reverse)
{
    cout << "TODO: Implement BubbleSort" << endl;
}
double doubleArray[] = {
    22.3, 11.2, 33.21, 44.2, 91.2, 15.2, 77.1, 8.2
};
int CompareDouble(double const & a, double const & b)
{
    return a > b ? 1 : a == b ? 0 : -1;
}
int main()
{
    auto fnCompare = [](double const & a, double const & b) -> int {
        return a > b ? 1 : a < b ? -1 : 0;
    };
    // compile OK:
    BubbleSort(doubleArray, CompareDouble, false);
    BubbleSort(doubleArray, static_cast<fnCompare_t<double>>(fnCompare), false);
    BubbleSort<double>(doubleArray, fnCompare, false);
    // compile error, could not deduce template argument:
    //BubbleSort(doubleArray, fnCompare, false);
    return 0;
}

的原因是因为使用扣除时,您无法对模板参数进行隐式转换。经典的例子是:

template <class T>
T min(T x, T y);

将此功能称为min(1, 3.0)将导致故障。因为对于这两个参数,它都试图找到T以获得完美的匹配和失败。如果明确指定模板参数可以工作:min<double>(1, 3.0)。在您的示例中,情况也是如此,如果您明确指定T,它将起作用。

编写功能签名的惯用方式是:

template <typename Iter, typename F>
inline void BubbleSort(
    Iter b, Iter e,
    F fnCompare,
    bool reverse)

但是,这会丢弃编译时长度信息。如果要保留它,可以做:

template <typename T, size_t count, typename F>
inline void BubbleSort(
    T(&array)[count],
    F fnCompare,
    bool reverse);

尽管您至少应该考虑使用std::array而不是C样式数组,这会使签名少一些丑陋,并且具有其他好处。

这似乎很奇怪,因为我们没有按照我们的签名"验证"具有正确签名的比较器。但这在C 中是正常的,如果比较器不正确,则在使用时会失败,并且仍然是编译时间错误。还要注意,当您尝试依靠lambda隐式转换为函数指针时,您是不必要的限制性的:lambdas仅通过相同的签名将功能指针转换为函数指针。即使Lambda的输出可隐式转换为功能指针的输出,即使Lambda仍然可以使用!

,您的lambda也不会隐式转换!

作为最后的说明,通常最好通过函子,因为它对性能更好。比较器通常是很小的功能,通常会被内衬。但是在您的版本中,比较器通常不会夹住,在我的版本中,它将(因为我保留了lambda的原始类型,而您却没有(。

您需要将lambda明确施放到功能指针上。没有其他方法可以解决。但是,您可以将+应用于lambda,而不是static_cast,这将触发功能指针转换,因为您可以将+应用于指针类型:

BubbleSort(doubleArray, +fnCompare, false);
//                      ^^
//     applying unary + invokes the function pointer conversion operator

为什么没有隐含呼吁转换操作员的原因是,在超载分辨率期间,编译器只会考虑完美匹配的模板(有关更多信息,请参见此信息(。由于lambda不是功能指针,因此不能有完美的匹配,并且超载被丢弃。