C++中auto的那些“雷区”,仿函数里藏着的类型陷阱
相信大家在写C++排序时经常会使用算法库中sort,但是实际应用中我们很少只对数字进行排序,更多的是对一个封装了多种变量的结构体进行排序,那么此时我们就得要自己定义排序规则,以达到我们的排序目的,于是sort的第三个参数就对我们有了重大意义,可以将我们写好的仿函数放到第三个参数中,达到正确的排序目的,接下来,我会一一讲解我在写仿函数代码时所遇到的问题。
根据需求,我需要对一个pair嵌套pair类型的vector容器进行排序,如下
vector<pair<int, pair<string, int>>>v(mp.begin(), mp.end());
sort(v.begin(), v.end(), compare());
类型相当复杂,传统的sort排序已经无法满足需求,而是需要自己定义仿函数来决定排序规则,我的仿函数逻辑如下
struct compare
{
bool operator()(const pair<int, pair<string, int>>& kv1, const pair<int, pair<string, int>>& kv2)
{
if (kv1.second.second != kv2.second.second)
{
return kv1.second.second > kv2.second.second;
}
if (kv1.second.first != kv2.second.first)
{
return kv1.second.first > kv2.second.first;
}
return kv1.first < kv2.first;
}
};
你其实就会发现,这逻辑还行,但是运算符重载函数的两个参数怎么这么冗长???此时,据博主所学,首先想到的解决方案是使用函数模板,可以有效解决参数冗长的问题,如下
template<typename T>
struct compare
{
bool operator()(const T& kv1, const T& kv2)
{
if (kv1.second.second != kv2.second.second)
{
return kv1.second.second > kv2.second.second;
}
if (kv1.second.first != kv2.second.first)
{
return kv1.second.first > kv2.second.first;
}
return kv1.first < kv2.first;
}
但是在调用仿函数的时候需要进行模板实例化,否则无法识别参数类型
vector<pair<int, pair<string, int>>>v(mp.begin(), mp.end());
sort(v.begin(), v.end(), compare<pair<int, pair<string, int>>>());//模板实例化
到这里为止,这个问题算是解决了,但是我想到了C++14之后auto可以自动推导函数参数类型了,那我是不是就可以用auto替换掉这一堆冗长的类型了呢?试试看
事实证明,用auto并不可取
但这是为什么呢?原来,C++14之后auto确实可以自动推导函数参数类型,也可以推导函数的返回值,但是仅仅适用于普通函数,不可以用于结构体或类成员函数!!!这是语法所不允许的,尤其是仿函数,它是用于STL库或者算法库的参数的,需要明确参数类型才可以编译,auto本质上并不是明确的参数类型,因此会出现编译报错。
但如果是普通函数,则不会出现报错情况,但需要C++14的环境支持,如下
#include<iostream>
using namespace std;
//C++14以上才不会报错
auto MySwap(auto& x, auto& y)
{
auto tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10, b = 50;
MySwap(a, b);
cout << a << " " << b << endl;
return 0;
}
因此,仿函数用auto自动推导类型是不可取的,无论在哪个环境都不允许,但是我们坚持要用auto推导的形式那我们可以换一种写法,那就是lambda表达式代替仿函数放在sort的第三个参数中,更简便,更直观,也可以使用auto自动推导类型,达到了我们的目的
vector<pair<int, pair<string, int>>>v(mp.begin(), mp.end());
sort(v.begin(), v.end(), [](const auto& kv1, const auto& kv2)
{
if (kv1.second.second != kv2.second.second)
{
return kv1.second.second > kv2.second.second;
}
if (kv1.second.first != kv2.second.first)
{
return kv1.second.first > kv2.second.first;
}
return kv1.first < kv2.first;
});
但需要注意的是lambda表达式是C++11之后才出现的语法,而auto自动推导函数参数类型是C++14之后才出现的特性,因此要编译上面的代码,至少得需要C++14以上版本的编译环境才可以运行。但有些编译器(比如博主使用的vs2022)在C++11环境就可以编译通过可能是编译器通过拓展语法支持了泛型lambda(即auto参数),但这并不是C++11标准的内容。
本次讲解到此结束,看到这里想必你对auto的灵活运用又加深了不少的理解。
更多推荐


所有评论(0)