关于return

值是如何被返回的

值被返回的方式和变量和parameter被初始化的方式一样,即返回值是用作初始化一个临时变量。

牢记在返回局部变量的函数中的初始化规则很重要。比如,

string make_plural(size_t ctr, const string &word, const string &ending)
{
    return (ctr > 1) ? word + ending : word;
}

这个函数返回类型是 string,意味着这个返回值会被拷贝到函数调用点(call site)。这个函数返回word的拷贝, 或者一个没有名字的临时string对象。

而当函数返回一个引用时,这个引用和普通引用一样,即只是另一个对象的别名。

const string &shorterString(const string &s1, const string &s2)
{
    return s1.size() <= s2.size() ? s1 : s2;
}

因为返回的是引用,返回时不需要拷贝。

不要返回临时对象的指针或引用!!

理由很简单,函数结束时,其存储空间会被释放。这样不管是指针还是引用都是不合法的。

const string &manip()
{
    string ret;
    if (!ret.empty())
       return ret; // error
    else
       return "Empty"; // error
}
}

另外,关于返回值的左值和右值问题:

仅当返回引用时,返回的是左值;其它都是右值。

返回值的列表解析

C++ 11可以返回一个使用列表解析的值。

vector<string> process()
{
    // ...
    return {'function', 'okey'};
    // or: return {};
}

只有能使用列表解析方法初始化的都能使用这种方法,再比如:

int testReturnList()
{
    return {9};
}

返回指向数组的指针

因为数组不能复制,所有不能直接返回数组。

但是,我们可以返回指向数组的指针或引用。但不幸的是,定义这样的函数类型比较麻烦。但可以使用类型别名的方法简化。

typedef int arrT[10]; // arrT是有十个int数组类型的别名
using arrtT = int[10]; // 和上面等价
arrT* func(int i); // 函数返回的是指向有十个int数组的指针

当不使用类型别名时,要记住:函数名后要有数组维数

回忆一下:

int arr[10];
int *p1[10]; // p1有十个指针的数组
int (*p2)[10] = &arr; // 这个才是 指向有十个int数组的指针

因此,这时函数的声明比较复杂:

Type (*function(parameter_list))[dimension]

举个例子:

int (*func(int i))[10];

嗯,这个比较复杂,可以这样去理解这个声明:

  • func(int)表明我们可以使用一个int类型参数调用函数func
  • (*func(int))表明我们可以解引用这个调用的结果,即返回的的是指针
  • (*func(int))[10]表明解引用后的结果产生一个大小为10的数组
  • int (*func(int i))[10]表明数组的类型是int

使用尾返回类型(trailing return type)

这是C++ 11的新特性。方法如下:

auto func(int i) -> int(*)[10];

即函数后前使用auto,parameter列表后紧跟-> 返回类型

当然,使用deltype又可以。比如,

int odd[] = {1, 3, 5};
int even[] = {2, 4, 6};
decltype(odd) *arrPrt(int i)
{
    return (i % 2) ? &odd : &even;
}

我们需要记住的是,deltype不会自动把数组转成对应的指针