什么时候可以自动退货?

时间:2014-05-31 09:34:38

标签: c++ c++14

有哪些规则,允许在c ++ 1y中使用writing自动返回类型?

#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;
}

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

2 个答案:

答案 0 :(得分:7)

  

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

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

  

T成为变量的声明类型或返回类型   功能。如果占位符是auto 类型说明符,则推导出   type是使用模板参数推导的规则确定的。如果   扣除是针对return语句而初始值设定项是    braced-init-list (8.5.4),该程序格式错误。除此以外,   通过将P的出现替换为新值,从T获取auto   发明了类型模板参数U,或者,如果初始化器是a    braced-init-list std::initializer_list<U>。推断一个值   U使用函数调用中的模板参数推导规则   (14.8.2.1),其中P是函数模板参数类型和   初始化程序是相应的参数。如果扣除失败,那么   声明是不正确的。否则,推断出的类型   变量或返回类型是通过将推导出的U代入   P

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

  

如果具有声明的返回类型的函数包含占位符   type有多个return语句,推导出返回类型   每个return声明。如果推断出的类型在每个中都不相同   扣除,该计划是不正确的。

所以,对于这段代码:

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 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语句之后具有递归调用。如有必要,可以使用相同的类型进行投射。

答案 1 :(得分:3)

<强>简介

有一些简单的规则说明何时可以从函数体推导出函数的 return-type ,并在auto时推断出适用于 return-type

这些规则都在标准(n3797 [1] 中说明,并且每篇规则都列在本文其余部分的自己的部分中。


7.1.6.4 自动说明符 [dcl.type.elab]

[1]

有什么不能使用auto作为返回类型推断出来吗?

  

[dcl.type.elab]p1 如果扣除的是return语句且初始化程序是braced-init-list(8.5.4),则程序格式错误。

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



如果某个函数有多个 return-statement ,将推断出哪种类型?

  

[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)格式不正确,因为所有 return-statements 的类型不同,而< em>(2)是合法的,因为两个 return-statements 产生相同的类型。



如果该功能没有 return-statement 会怎样?

  

[dcl.type.elab]p10 如果具有使用占位符类型的声明返回类型的函数没有返回语句,则推断返回类型,就好像从返回语句中结束时没有操作数一样功能体的支撑。

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;
}

注意:我们无法在junc内获取foo的地址,因为这样做需要了解junc的完整类型是什么,在我们提供了推断出返回类型的定义之前,我们不知道的事情。 (2)是合法的,而(1)不是

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