8.8 - 函数的类型推断
Key Takeaway
- 在 C++14 中,
auto
可以被用于函数返回值类型,此时需要保证所有返回值类型是一致的,否则需要进行转换 - 函数返回值类型使用
auto
的最大缺点这些函数在使用前必须已经完整定义(只有前向声明)是不够的。这也意味着auto
类型返回值的函数通常只能够在定义它们的文件中使用。 auto add(int x, int y) -> int
后置返回值类型。这种形式可以对齐函数名或是配合 lambda 使用- 函数形参的类型不能使用类型推断
考虑下面代码:
1 2 3 4 |
|
但编译这个函数的时候,编译器可以确定 x + y
的求值结果为 int
类型,然后它会确保函数的返回值类型和该类型匹配(或者返回值的类型可以被转换为函数声明的返回类型)。
因为编译器已经能够从 return
语句推断返回值的类型,所以在 C++14 中,auto
可以被用于函数返回值类型,将 auto
关键字放置在原来用于声明函数返回值类型的地方就可以。
例如:
1 2 3 4 |
|
由于 return
语句返回的是一个 int
类型的值,则编译器会将函数返回值的类型推断为 int
。
当使用 auto
返回值类型是,所有返回值的类型必须是一致的,否则会造成错误,例如:
1 2 3 4 5 6 7 |
|
在上面的函数中,由于两个 return
语句返回值的类型不同,所以编译器会提示错误。
如果有些情况下需要使用不同的返回值类型,那么要么你为函数显式地指明返回值类型(此时编译器会将不匹配的返回值进行隐式类型转换),或者你可以显式地将 return
语句的返回值转换成相同的类型。在上面的例子中,我们可以将 5
修改为 5.0
,或者对于非字面量的情况还可以使用 static_cast
函数返回值类型使用 auto
的最大缺点这些函数在使用前必须已经完整定义(只有前向声明)是不够的。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
在作者的电脑上会产生如下编译错误:
1 |
|
这是理所当然的,因为仅凭前向声明编译器无法对函数的返回值类型进行推断。这也意味着 auto
类型返回值的函数通常只能够在定义它们的文件中使用。
与对象的类型推断不同,对于函数返回类型推断的最佳实践并没有太多共识。当对对象使用类型推断时,初始化式总是作为同一语句的一部分出现,因此确定要推断的类型通常不会太麻烦。对于函数,情况就不是这样了——当查看函数的原型时,没有上下文来帮助指示函数返回的类型。虽然一个优秀的 IDE 应该清楚推断出的函数类型是什么,但在没有这种功能的时候,用户实际上必须深入到函数体本身来确定函数返回的类型。犯错误的几率更高。此外,函数返回值的类型推断不能通过前向声明使用,这也限制了它们在多文件程序中的可用性。
最佳实践
对于一般函数来说,最好使用显式的函数返回值类型而不是类型推断。
后置返回值类型语法
auto
也可以用于后置返回值类型语法,即函数的返回值类型被放置在函数原型的后面。
考虑下面的代码:
1 2 3 4 |
|
它等价于下面后置返回值语法形式:
1 2 3 4 |
|
在这个例子中,auto
不再被用来进行类型推断——它只是后置返回值类型语法的一部分。
这种语法有什么用呢?
这种语法可以帮我们对齐函数名:
1 2 3 4 |
|
此外,一些 C++ 高级特性也需要后置返回值类型语法的支持,例如 lambda 表达式(参见: 12.7 -- Introduction to lambdas (anonymous functions) )
就目前而言,我们还是建议大家使用传统的函数返回值语法,除非是需要配合一些特殊的特性来使用时。
## 函数形参的类型不能使用类型推断
很多新手程序员在学习过类型推断后,会尝试这样做:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
不幸的是,类型推演不适用于函数形参,在++ 20 之前,上面的程序无法编译(会将得到关于函数形参不能具有自动类型的错误)。
在 C++20 中,auto
关键字再次得到了扩展,所以上述程序可以正常编译了。不过,此时 auto
并不是被用做类型推断,而是用于触发函数模板(function template)特性。
相关内容
我们会在8.13 - 函数模板中介绍函数模板并在8.15 - 具有多种类型的函数模板中讨论 auto
在此语境中的用途。