我有一个冒泡排序函数,它接受一个数组,一个比较函数和一个布尔值,指示它是否应该对数组进行倒置排序.它是一个支持任何数据类型的模板函数,并将自动推导出数组大小.
指定比较函数时,如果我传递函数指针,编译器会自动推导出数组的数据类型,这很好.但是,如果我改为传递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;
}
Run Code Online (Sandbox Code Playgroud)
原因是因为在使用演绎时无法对模板化参数进行隐式转换.经典的例子是:
template <class T>
T min(T x, T y);
Run Code Online (Sandbox Code Playgroud)
调用此函数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)
Run Code Online (Sandbox Code Playgroud)
但是,这会丢弃编译时间长度信息.如果你想保留它,你可以这样做:
template <typename T, size_t count, typename F>
inline void BubbleSort(
T(&array)[count],
F fnCompare,
bool reverse);
Run Code Online (Sandbox Code Playgroud)
虽然你至少应该考虑使用std::array而不是C风格的数组,这将使签名不那么难看并具有其他好处.
这可能看起来很奇怪,因为我们没有在我们的签名中"验证"具有正确签名的比较器.但这在C++中是正常的,如果比较器不正确,那么它将在使用点失败并且仍然是编译时错误.当您尝试依赖于lambda隐式转换为函数指针时,请注意,您将受到不必要的限制:lambdas仅转换为具有相同签名的函数指针.即使lambda的输出可以隐式转换为函数指针的输出,你的lambda也不会隐式转换,即使lambda仍然可以使用!
作为最后的最后一点,最好通过仿函数,因为它对性能更好.比较器通常是小功能,通常会内联.但是在你的版本中,比较器通常不会被内联,因为我会保留它(因为我保留了lambda的原始类型,而你没有).
| 归档时间: |
|
| 查看次数: |
700 次 |
| 最近记录: |