1 传统的泛型函数返回值处理
C89 的语法对函数定义比较宽松,允许不指定函数返回值类型,编译器会默认函数返回值是 int (default-int)类型。但是 C++ 不允许默认返回值类型,也就是说在 C++ 代码中定义一个函数,必须指定函数的返回值类型。这种强类型语法要求对于保证代码语义的正确性很有帮助,但是也给编写泛型或模板代码带来不小的挑战。因为即使是函数模板也必须遵守这个要求,使得函数返回值如果和模板参数类型有相关性的时候非常难处理。你一定见过类似这样的代码:
template <class R, class U, class T>
R MyAdd(const U& u, const T& t)
{
return (u + t);
}
int r = MyAdd<int>(4, 9); //int + int
double r = MyAdd<double>(4, 2.1); //int + double
虽然这样也能解决返回值 R 的类型参数化的需求,但是在使用上很繁琐,有多此一举之感。
2 C++ 11 的返回值类型后置
C++ 11 引入了一种新的语法形式,就是返回值类型后置(trailing-return-type)语法,也被称为跟踪返回值类型语法或后置返回值类型。可以借助返回值类型后置语法,配合 decltype() 对上一节的例子进行简化。返回值类型后置还可以用于普通的函数定义和 lambda 函数,应对复杂返回值类型,提高代码可读性,还可以借助 decltype() 实现返回值类型的自动推导。
2.1 用于泛型函数
返回值类型后置语法对于泛型函数非常有用,返回值类型后置通常结合 auto 和 decltype() 一起使用,比如上一节介绍的 MyAdd<>() 函数,可以这样实现:
template <class U, class T>
auto MyAdd(const U& u, const T& t) ->decltype(u+t)
{
return (u + t);
}
在使用的时候不再需要通过模板参数指定返回值类型:
int r = MyAdd(4, 9);
double r = MyAdd(4, 2.1); //自动提升为 double
2.2 用于 lambda 函数
返回值类型后置语法不仅解决了泛型函数定义难题,还能让 lambda 函数的定义在形式上更自然。lambda 函数的返回值类型一般可由编译期自动推导,比如:
auto Func = [&a](int b) { return (a + b); };
若捕捉的外部变量 a 定义为 int 时,Func 的类型就是:int Func(int),返回值类型是 int:
int a = 10;
std::cout << "Func(3) = " << Func(3) << std::endl; //输出 13
若捕捉的外部变量 a 定义为 double 时(a+b 会做数据类型提升),Func 的类型就是:double Func(int),返回值类型是 double:
double a = 10.6;
std::cout << "Func(3) = " << Func(3) << std::endl; //输出 13.6
当然,也可以通过返回值类型后置语法给这个 Lambda 函数指定 int 作为返回值类型,若 a 为 double 类型,则 a+b 的计算提升为 double 类型后再返回整数取整运算后的结果,则数据截断处理:
auto Func = [&a](int b) ->int { return (a + b); };
2.3 用于普通函数
大多数情况下,普通函数的返回值都可以准确推定,所以用不到返回值类型后置语法,但是对于一些返回值类型比较特殊情况,用返回值类型后置能使代码更容易理解,比如这个返回值是函数指针的例子:
int (*Foo(char* name))(double)
{
return nullptr;
}
这是一个令人毛骨悚然的函数定义,看不懂这个的人是不敢称自己精通 C/C++ 语言。但是对于大多数开发人员来说,这样做没必要,很多开发团队也会禁止这样的语法使用,因为有更容易理解的替代方案,那就是 typedef:
typedef int (*FuncPtr)(double);
FuncPtr Foo(char* name)
{
return nullptr;
}
用 typedef 的缺点就是麻烦一点,需要多写一行函数指针的类型定义,现在有了返回值类型后置语法,typedef 这一行就可以省了:
auto Foo3(char* name) -> int (*)(double)
{
return nullptr;
}
并且代码的可读性没有降低,从某种程度上说,还提高了代码的可读性。
3 C++ 14 的改进
C++ 14 的改进就是可以在模板参数中使用 auto,然后配合 decltype() 实现类型的自动推导,在一定程度上可以省去后置返回值类型的形式,这样更省事儿了。
关注作者的算法专栏
https://blog.csdn.net/orbit/category_10400723.html
关注作者的出版物《算法的乐趣(第二版)》
https://www.ituring.com.cn/book/3180