何时可以应用自动返回类型

When can automatic return type apply?

本文关键字:返回类型 应用 何时可      更新时间:2023-10-16
允许

在 c++1y 中编写自动返回类型的规则是什么

#include <iostream>
using namespace std;
template<typename T1, typename T2>
auto f(T1 const& a, T2 const &b)
{
    if (a > b) return a-b;
    else return a+b;
}
int main() 
{
    cout << f(1, 2.) << endl;
    return 0;
}

函数体的圈复杂度是否有限制?

函数的圈复杂度是否有限制 身体?

标准规定的内容 (N3797, §7.1.6.4(:

T变量的声明类型或返回类型 功能。 如果占位符是auto类型说明符,则推导 类型是使用模板参数推断规则确定的。如果 扣除额用于 return 语句,初始值设定项为 大括号初始化列表 (8.5.4(,程序格式不正确。 否则 T通过将auto的出现次数替换为新的P 发明的类型模板参数U或者,如果初始值设定项是 带大括号的初始化列表,带有std::initializer_list<U> .推导一个值 U使用从函数调用中扣除模板参数的规则 (14.8.2.1(,其中P是函数模板参数类型,而 初始值设定项是相应的参数。如果扣除失败, 声明格式不正确。否则,推导的类型为 变量或返回类型是通过将推导的U代入获得的 P .

因此,tl;dr:返回类型是通过模板参数推导从return语句中的表达式推导出来的。有一个虚构的模板,它被调用,return语句中的表达式作为函数参数,推导的模板参数U将成为占位符返回类型中 auto 的替代品。现在,如果我们有多个返回语句会发生什么?简单:我们推断出每个return语句,并检查它们是否兼容:

如果函数具有包含占位符的声明返回类型 类型有多个return语句,返回类型推导为 每个return语句。如果推导的类型在每个中都不相同 演绎,n程序格式不正确。

因此,对于此代码:

template<typename T1, typename T2>
auto f(T1 const& a, T2 const &b)
{
    if (a > b) return a-b;
    else return a+b;
}

进行以下扣除:

template<typename U>
void g(U);
g( a-b ); 
g( a+b );
// here, a and b have the exact same types as in a specialization of the template above.

当且仅当在两次调用中推导相同的模板参数时,代码格式正确。否则,扣除失败。如果使用 auto 说明符设置的返回类型不是简单的auto而是例如 auto const& ,则虚构模板的参数g具有相应的形式:

template<typename U>
void g(U const&);

电话将是相同的。同样,如果推导的U不同,则代码格式不正确。

如果您没有返回语句,则推导出的返回类型将void,根据

如果函数具有使用占位符类型的声明返回类型 没有return语句,返回类型推导自 在右大括号处没有操作数的return语句 函数体。

递归

如果你想要递归函数,它会变得更加棘手:

auto f( int a, int b )
{
    return a? b + a : f(a-1, b); // This is ill-formed!
}

这个问题可以用以下引用来解释:

如果需要具有未推断占位符类型的实体的类型 为了确定表达式的类型,程序格式不正确。 但是,一旦在函数中看到return语句,则 从该语句推导出的返回类型可用于其余部分 函数,包括在其他return语句中。

所以我们写:

auto f( int a, int b )
{
    if( a )
        return b + a;
    return f(a-1, b);
}

结论:

您可以使用任意复杂的函数,只要return语句在演绎过程中都产生相同的类型,并且递归函数在一些非递归return -语句之后具有递归调用。如有必要,请强制转换以获得相同的类型。

简介

有一些简单的规则可以说明何时可以从函数中推断出函数的返回类型,以及何时auto适用于返回类型

这些规则都在标准 (n3797( [1] 中陈述,每个规则都列在本文其余部分的单独部分中。

[1] 在第 7.1.6.4 节中,自动说明符 [dcl.type.elab]


有什么东西不能

auto作为返回类型来推断吗?

[dcl.type.elab]p1 如果扣除是针对 return 语句的,并且初始值设定项是大括号的初始化列表 (8.5.4(,则程序格式不正确。

auto func () { return {1,2,3}; } // ill-formed


如果一个函数有多个返回语句,将推导出哪种类型?

[dcl.type.elab]p9 如果声明的返回类型包含占位符类型的函数具有多个 return 语句,则为每个 return 语句推导返回类型。如果推导的类型在每个推导中都不相同,则程序格式不正确。

auto gunc_1 (bool val) { // (1), ill-formed
  if (val) return 123;
  else     return 3.14f;
}

auto gunc_2 (bool val) { // (2), legal
  if (val) return static_cast<float> (123);
  else     return 3.14f;
}

注意(1( 格式不正确,因为所有返回语句都不是同一类型,而 (2( 是合法的,因为两个返回语句产生相同的类型。


如果函数没有返回语句会发生什么情况?

[dcl.type.elab]p10 如果声明返回类型的函数使用占位符类型没有 return 语句,则返回类型就像从函数体右大括号处没有操作数的 return 语句中推导的那样。

auto hunc () { } // legal, return-type is `void`


在推导出返回类型之前,我可以使用该函数吗?

[dcl.type.elab]p11 如果需要具有未推导占位符类型的实体的类型来确定表达式的类型,则程序格式不正确。但是,一旦在函数中看到 return 语句,从该语句推导出的返回类型就可以用于函数的其余部分,包括其他 return 语句。

auto junc (); // declaration
void foo () { &junc; } // (1), ill-formed
auto junc () { // definition
   return 123;
}
void bar () { &junc; } // (2), legal

auto recursive (int x) {
  if (--x) return x + recursive (x); // (3), ill-formed
  else     return 0;
}

注意:我们不能在foo中获取junc的地址,因为这样做需要了解完整的junc类型是什么,直到我们提供了推断返回类型的定义才能知道。(2(因此是合法的,而(1(不是。

注意(3( 也是格式错误的,因为此时我们必须知道recursive的返回类型,但不知道。但是,以相反的顺序使用返回语句将是有效的。这样编译器就会知道recursive在遇到return x + recursive (x)时返回int