不要返回局部对象的引用或指针

函数完成后它所占用的存储空间也会被释放掉,因此局部变量的引用将指向不再有效的内存区域:

  1. // 严重错误: 试图返回局部对象的引用
  2. const string &foo() {
  3. string ret;
  4. if (!ret.empty()) {
  5. return ret; // 错误: 返回局部对象的引用
  6. } else {
  7. return "Empty"; // 错误: 返回局部对象的引用
  8. }
  9. }

列表初始化返回值

C++11新标准规定,函数可以通过列表初始化来对函数返回的临时量进行初始化:

  1. #include <string>
  2. #include <vector>
  3. std::vector<std::string> foo(int i) {
  4. if (i < 5) {
  5. return {}; // 返回一个空vector对象
  6. }
  7. return {"tomo", "cat", "tomocat"}; // 返回列表初始化的vector对象
  8. }
  9. int main() {
  10. foo(10);
  11. }

main函数返回值

main函数的返回值可以看成是状态指示器,返回0表示成功,返回其他值表示失败。cstdlib头文件定义了两个预处理变量,分别表示成功和失败:

  1. int main() {
  2. if (some_failure) {
  3. return EXIT_FAILURE;
  4. } else {
  5. return EXIT_SUCCESS:
  6. }
  7. }

返回函数指针

由于数组不能拷贝,因此函数不能返回数组,不过可以返回数组的指针或者引用。想要定义一个返回数组的引用或者指针的函数比较繁琐,不过我们可以使用类型别名来简化这一任务:

  1. // arrT: 包含10个整型元素数组的类型别名
  2. typedef int arrT[10];
  3. // arrT的等价声明
  4. using arrT = int[10];
  5. arrT* func(int i); // 返回指向10个整数的数组的指针

如果不使用类型别名,那么相同的函数我们需要写成:

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

C++11允许我们使用尾置返回类型:

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

还有一种情况是如果我们直到函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型:

  1. int odd[] = {1, 3, 5, 7, 9};
  2. int even[] = {0, 2, 4, 6, 8};
  3. // 根据i指向的不同返回两个已知数组中的一个
  4. decltype(odd) *arrPtr(int i) {
  5. return (i % 2) ? &odd : &even;
  6. }

尾置返回类型

编码规范:只有在常规写法(返回类型前置)不便于书写或者不便于阅读时才使用返回类型后置语法。

C++现在允许两种不同的函数声明方式,以往的写法是将返回类型置于函数名之前:

  1. int foo(int x);

C++11新标准引入了尾置返回类型,可以在函数名前使用auto关键字,在参数列表之后后置返回类型,例如:

Tips:尾置返回类型是显式地指定Lambda表达式返回值的唯一方式,当返回类型依赖模板参数时也可以使用使用尾置返回类型。

  1. // 普通函数
  2. auto foo(int x) -> int;
  3. // lambda表达式
  4. auto f = []() -> int { return 42; };
  5. // 模型函数
  6. template <class T, class U> auto add(T t, U u) -> decltype(t + u);